فهرست منبع

提交+合并5-27

rui.jiang 2 ماه پیش
والد
کامیت
3ce948b3a8
51فایلهای تغییر یافته به همراه3422 افزوده شده و 645 حذف شده
  1. BIN
      downLoadServer/downLoadServer.textClipping
  2. 9 2
      downLoadServer/package.json
  3. BIN
      downLoadServer/src/public/file/副本XXX风电场可靠性和能效双提升数据分析报告(模板).docx
  4. 42 0
      downLoadServer/src/public/js/echarts.min.js
  5. 285 8
      downLoadServer/src/server/controllers/chartController.js
  6. 13 17
      downLoadServer/src/server/controllers/uploadController.js
  7. 28 0
      downLoadServer/src/server/routes/chartRoutes.js
  8. 2 2
      downLoadServer/src/server/routes/exampleRoutes.js
  9. 1 1
      downLoadServer/src/server/server.js
  10. 15 15
      downLoadServer/src/server/utils/chartsCom/3DDrawingChart.js
  11. 3 2
      downLoadServer/src/server/utils/chartsCom/BarChart.js
  12. 238 0
      downLoadServer/src/server/utils/chartsCom/BoxLineCharts.js
  13. 206 0
      downLoadServer/src/server/utils/chartsCom/BoxMarkersCharts.js
  14. 4 3
      downLoadServer/src/server/utils/chartsCom/ColorbarInitTwoDmarkersChart.js
  15. 181 0
      downLoadServer/src/server/utils/chartsCom/FaultAll.js
  16. 181 0
      downLoadServer/src/server/utils/chartsCom/FaultUnit.js
  17. 183 0
      downLoadServer/src/server/utils/chartsCom/GeneratorTemperature.js
  18. 4 3
      downLoadServer/src/server/utils/chartsCom/HeatmapCharts.js
  19. 183 0
      downLoadServer/src/server/utils/chartsCom/PlotlyCharts.js
  20. 217 0
      downLoadServer/src/server/utils/chartsCom/PlotlyChartsFen.js
  21. 152 166
      downLoadServer/src/server/utils/chartsCom/Radar.js
  22. 17 16
      downLoadServer/src/server/utils/chartsCom/Time3DChart.js
  23. 3 2
      downLoadServer/src/server/utils/chartsCom/TwoDMarkersChart.js
  24. 3 2
      downLoadServer/src/server/utils/chartsCom/TwoDMarkersChart1.js
  25. 185 0
      downLoadServer/src/server/utils/chartsCom/WindRoseChart.js
  26. 181 0
      downLoadServer/src/server/utils/chartsCom/YewErrorBarChart.js
  27. 3 17
      downLoadServer/src/server/utils/chartsCom/lineAndChildLine.js
  28. 8 19
      downLoadServer/src/server/utils/chartsCom/lineChartsFen.js
  29. 4 4
      downLoadServer/src/server/utils/chartsCom/powerMarkers2DCharts.js
  30. 166 0
      downLoadServer/src/server/utils/chartsCom/yawErrorBarSum.js
  31. 190 0
      downLoadServer/src/server/utils/chartsCom/yawErrorLine.js
  32. 171 0
      downLoadServer/src/server/utils/copyFileCsv.js
  33. 1 16
      downLoadServer/src/server/utils/minioService.js
  34. 2 2
      src/components/map/index.vue
  35. 25 0
      src/utils/common.js
  36. 34 0
      src/utils/downloadFile.js
  37. 0 1
      src/views/home/Index.vue
  38. 10 9
      src/views/overview/components/fault_all/index.vue
  39. 5 3
      src/views/overview/components/wind_speed_frequency/index.vue
  40. 2 1
      src/views/overview/components/yaw_error_density/index.vue
  41. 300 93
      src/views/performance/assetssMag.vue
  42. 7 2
      src/views/performance/components/PlotlyCharts.vue
  43. 78 82
      src/views/performance/components/chartsCom/BoxMarkersCharts.vue
  44. 1 115
      src/views/performance/components/chartsCom/FaultAll.vue
  45. 0 8
      src/views/performance/components/chartsCom/FaultUnit.vue
  46. 1 1
      src/views/performance/components/chartsCom/HeatmapCharts.vue
  47. 1 0
      src/views/performance/components/chartsCom/WindRoseChart.vue
  48. 1 1
      src/views/performance/components/chartsCom/yawErrorBarSum.vue
  49. 7 7
      src/views/performance/components/chartsCom/yawErrorLine.vue
  50. 66 22
      src/views/performance/js/allAnalysisType.js
  51. 3 3
      vue.config.js

BIN
downLoadServer/downLoadServer.textClipping


+ 9 - 2
downLoadServer/package.json

@@ -12,15 +12,22 @@
   "author": "",
   "license": "ISC",
   "dependencies": {
-    "axios": "^1.6.8",
+    "axios": "^1.9.0",
     "cors": "^2.8.5",
+    "docx": "^9.5.0",
+    "docxtemplater": "^3.62.2",
+    "docxtemplater-image-module-free": "^1.1.1",
     "dotenv": "^16.5.0",
+    "echarts": "^5.6.0",
     "express": "^4.19.1",
     "form-data": "^4.0.2",
-    "fs-extra": "^11.2.0",
+    "fs-extra": "^11.3.0",
+    "image-size": "^2.0.2",
     "minio": "^8.0.5",
     "multer": "^1.4.5-lts.2",
     "node-plotly.js": "^0.0.1",
+    "papaparse": "^5.5.2",
+    "pizzip": "^3.2.0",
     "plotly.js": "^3.0.1",
     "plotly.js-dist": "^2.34.0",
     "plotly.js-dist-min": "^2.34.0",

BIN
downLoadServer/src/public/file/副本XXX风电场可靠性和能效双提升数据分析报告(模板).docx


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 42 - 0
downLoadServer/src/public/js/echarts.min.js


+ 285 - 8
downLoadServer/src/server/controllers/chartController.js

@@ -10,7 +10,24 @@ import { generateTwoDMarkersChart1 } from "../utils/chartsCom/TwoDMarkersChart1.
 import { generateColorbarInitTwoDmarkersChart } from "../utils/chartsCom/ColorbarInitTwoDmarkersChart.js";
 import { generatepowerMarkers2DCharts } from "../utils/chartsCom/powerMarkers2DCharts.js";
 import { getRadarCharts } from "../utils/chartsCom/Radar.js";
+import { generateBoxLineChart } from "../utils/chartsCom/BoxLineCharts.js";
+import { generateBoxMarkersCharts } from "../utils/chartsCom/BoxMarkersCharts.js";
+import { generateGeneratorTemperature } from "../utils/chartsCom/GeneratorTemperature.js";
+import { getWindRoseChart } from "../utils/chartsCom/WindRoseChart.js";
+import { generateYawErrorLine } from "../utils/chartsCom/yawErrorLine.js";
+import { getFaultAllCharts } from "../utils/chartsCom/FaultAll.js";
+import { getFaultUnitCharts } from "../utils/chartsCom/FaultUnit.js";
+import { getYawErrorBarSumCharts } from "../utils/chartsCom/yawErrorBarSum.js";
+import { getYewErrorBarChart } from "../utils/chartsCom/YewErrorBarChart.js";
+import { generatePlotlyCharts } from "../utils/chartsCom/PlotlyCharts.js";
+import { generatePlotlyChartsFen } from "../utils/chartsCom/PlotlyChartsFen.js";
+// import { getProductionIndicatorTotal } from "../utils/chartsCom/productionIndicatorTotal.js";
+import { copyFileDocx } from "../utils/copyFileCsv.js";
 import axios from "axios";
+import Papa from "papaparse";
+import fs from "fs-extra";
+import path from "path";
+import FormData from "form-data";
 
 // 提取公共逻辑到辅助函数
 const handleChartGeneration = async (
@@ -21,7 +38,8 @@ const handleChartGeneration = async (
   additionalParams = {}
 ) => {
   try {
-    const { fileAddr, bucketName, objectName } = req.body;
+    const { fileAddr, bucketName, objectName, fieldInfo, fieldEngineCode } =
+      req.body;
     if (!fileAddr) {
       return errorResponse(res, "缺少数据URL", 400);
     }
@@ -43,13 +61,13 @@ const handleChartGeneration = async (
       data = response.data;
     }
     console.log(data, "  data");
+
     // 验证数据格式
     if (
       !data ||
       !data.data ||
       !data.data[0] ||
-      !data.data[0].xData ||
-      !data.data[0].yData
+      (!data.data[0].xData && !data.data[0].yData && !data.data[0].windRoseData)
     ) {
       return errorResponse(res, "获取的数据格式不正确", 400);
     }
@@ -59,13 +77,15 @@ const handleChartGeneration = async (
       data,
       bucketName,
       objectName,
+      fieldEngineCode,
+      fieldInfo,
       ...Object.values(additionalParams)
     );
-
+    console.log(imageUrl, "imageUrl");
     successResponse(
       res,
       {
-        imageUrl: imageUrl,
+        imageUrl,
       },
       successMessage
     );
@@ -86,6 +106,98 @@ const handleChartGeneration = async (
     }
   }
 };
+/**
+ * 下载并解析 CSV 数据(Node.js 版)
+ * @param {string} url - CSV 地址
+ * @param {function} onParsed - 解析成功回调,返回解析后的数据
+ * @param {function} onError - 出错时的回调
+ */
+const fetchCsvAndParse = async (
+  req,
+  res,
+  onParsed,
+  onError,
+  successMessage,
+  additionalParams = {}
+) => {
+  try {
+    const { fileAddr, bucketName, objectName, fieldInfo, chartType } = req.body;
+    if (!fileAddr) {
+      return errorResponse(res, "缺少数据URL", 400);
+    }
+
+    const response = await axios.get(fileAddr, { responseType: "text" });
+    const csvText = response.data;
+    console.log(req.body, "req.body");
+    Papa.parse(csvText, {
+      header: true,
+      complete: async (result) => {
+        const data = result.data
+          .filter((row) => Object.keys(row).length)
+          .slice(0, result.data.length - 1);
+        console.log(data, "csv data 数据");
+        // 调用 onParsed 函数并确保只发送一次响应
+        try {
+          let imageUrl = "";
+          let imageUrls = [];
+          if (typeof onParsed === "function") {
+            if (chartType === "radar") {
+              const results = await Promise.all(
+                data &&
+                  data.map((val) =>
+                    onParsed(
+                      val,
+                      data,
+                      bucketName,
+                      objectName,
+                      fieldInfo,
+                      additionalParams
+                    ).catch((err) => {
+                      console.error("getRadarCharts failed:", err);
+                      return null;
+                    })
+                  )
+              );
+              imageUrls.push(...results); // 如果你还需要存入 imageUrls
+              console.log(imageUrls, "img");
+            } else {
+              imageUrl = await onParsed(
+                data,
+                bucketName,
+                objectName,
+                fieldInfo,
+                additionalParams
+              );
+            }
+          }
+          successResponse(res, { data, imageUrl, imageUrls }, successMessage);
+        } catch (error) {
+          console.error("处理解析数据时出错:", error);
+          errorResponse(res, "处理数据失败", 500);
+        }
+      },
+      error: (error) => {
+        console.error("CSV 解析错误:", error);
+        if (onError) {
+          onError(error);
+        }
+        errorResponse(res, "CSV 解析失败", 500); // 发送错误响应
+      },
+    });
+  } catch (error) {
+    console.error("获取 CSV 错误:", error);
+
+    if (error.response) {
+      errorResponse(
+        res,
+        `获取数据失败: ${error.response.status}`,
+        error.response.status
+      );
+    } else {
+      errorResponse(res, "获取 CSV 失败", 500);
+    }
+  }
+};
 
 export const createBarChart = async (req, res) => {
   await handleChartGeneration(req, res, generateBarChart, "柱状图生成成功");
@@ -106,7 +218,23 @@ export const createLineAndChildLine = async (req, res) => {
     "折线图生成成功"
   );
 };
+//createPlotlyChartsFen
+export const createPlotlyChartsFen = async (req, res) => {
+  const { fieldEngineCode } = req.body;
+  await handleChartGeneration(
+    req,
+    res,
+    generatePlotlyChartsFen,
+    "折线图生成成功",
+    {
+      fieldEngineCode,
+    }
+  );
+};
 
+export const createPlotlyCharts = async (req, res) => {
+  await handleChartGeneration(req, res, generatePlotlyCharts, "折线图生成成功");
+};
 export const createHeatmapCharts = async (req, res) => {
   await handleChartGeneration(req, res, generateHeatmapChart, "热力图生成成功");
 };
@@ -132,7 +260,6 @@ export const createTwoDMarkersChart1 = async (req, res) => {
     "2D 分图生成成功"
   );
 };
-//
 export const createColorbarInitTwoDmarkersChart = async (req, res) => {
   await handleChartGeneration(
     req,
@@ -149,7 +276,157 @@ export const createpowerMarkers2DCharts = async (req, res) => {
     "2D 分图生成成功"
   );
 };
-//
+
+export const createBoxLineCharts = async (req, res) => {
+  await handleChartGeneration(req, res, generateBoxLineChart, "箱线图生成成功");
+};
+export const createBoxMarkersCharts = async (req, res) => {
+  await handleChartGeneration(
+    req,
+    res,
+    generateBoxMarkersCharts,
+    "箱线图生成成功"
+  );
+};
+
+export const createGeneratorTemperature = async (req, res) => {
+  await handleChartGeneration(
+    req,
+    res,
+    generateGeneratorTemperature,
+    "发电温度"
+  );
+};
+
+export const createGetWindRoseChart = async (req, res) => {
+  await handleChartGeneration(req, res, getWindRoseChart, "风向玫瑰图");
+};
+
+export const createYawErrorLine = async (req, res) => {
+  await handleChartGeneration(req, res, generateYawErrorLine, "静态偏航分图");
+};
+
+export const createFaultAllCharts = async (req, res) => {
+  await fetchCsvAndParse(req, res, getFaultAllCharts, "全场故障");
+};
+//getFaultUnitCharts
+export const createFaultUnitCharts = async (req, res) => {
+  await fetchCsvAndParse(req, res, getFaultUnitCharts, "机组故障");
+};
+
+export const createyawErrorCharts = async (req, res) => {
+  await fetchCsvAndParse(
+    req,
+    res,
+    getYawErrorBarSumCharts,
+    "静态偏航误差的绝对值机组台数分布情况"
+  );
+};
+export const createyawErrorBarSumCharts = async (req, res) => {
+  await fetchCsvAndParse(req, res, getYewErrorBarChart, "静态偏航误差值图");
+};
+//全场运行指标
+export const createProductionIndicatorTotal = async (req, res) => {
+  await fetchCsvAndParse(req, res, "", "全场运行指标");
+};
 export const createRadarCharts = async (req, res) => {
-  await handleChartGeneration(req, res, getRadarCharts, "雷达图生成成功");
+  await fetchCsvAndParse(req, res, getRadarCharts, "雷达图生成成功");
+};
+
+const MAX_FILE_SIZE = 100 * 1024 * 1024; // 10MB
+const ALLOWED_EXTENSIONS = [".docx"];
+const TEMP_FILE_DIR = path.join(process.cwd(), "src", "public", "file");
+
+export const createCopyFileCsv = async (req, res) => {
+  let result = null;
+  let tempFilePath = null;
+
+  try {
+    const { fieldInfo, bucketName, objectName } = req.body;
+
+    // 1. 参数验证
+    if (!fieldInfo || !bucketName || !objectName) {
+      return res.status(400).json({
+        success: false,
+        message: "缺少必要的参数",
+      });
+    }
+
+    // 2. 生成文档
+    result = await copyFileDocx(fieldInfo, req.body);
+    tempFilePath = result.url;
+
+    // 3. 验证生成的文件
+    const stats = await fs.stat(tempFilePath);
+
+    // 检查文件大小
+    if (stats.size > MAX_FILE_SIZE) {
+      throw new Error(
+        `生成的文件超过大小限制 (${MAX_FILE_SIZE / 1024 / 1024}MB)`
+      );
+    }
+
+    console.log("开始上传文件到 MinIO...");
+    // 直接调用上传接口,传递文件路径
+    const response = await axios.post(
+      `${process.env.API_BASE_URL}/examples/upload`,
+      {
+        filePath: tempFilePath,
+        bucketName,
+        objectName: `${objectName}/${path.basename(tempFilePath)}`,
+      }
+    );
+
+    if (!response?.data?.url) {
+      throw new Error("文件上传失败:未获取到URL");
+    }
+
+    console.log("文件上传成功");
+
+    // 4. 清理临时文件
+    await fs.unlink(tempFilePath).catch(console.error);
+    console.log("临时文件已清理");
+
+    // 5. 返回成功响应
+    res.json({
+      success: true,
+      message: response.data.url,
+      data: {
+        url: response.data.url,
+        fileName: path.basename(tempFilePath),
+        fileSize: stats.size,
+        uploadTime: new Date().toISOString(),
+      },
+    });
+  } catch (error) {
+    console.error("文件处理失败:", error);
+
+    // 6. 错误处理
+    let errorMessage = "文件处理失败";
+
+    if (error.response) {
+      errorMessage = `上传失败: ${
+        error.response.data?.message || error.message
+      }`;
+    } else if (error.code === "ENOENT") {
+      errorMessage = "文件不存在或无法访问";
+    } else if (error.code === "EACCES") {
+      errorMessage = "文件访问权限不足";
+    } else {
+      errorMessage = error.message;
+    }
+
+    // 7. 清理临时文件
+    if (tempFilePath) {
+      await fs.unlink(tempFilePath).catch(console.error);
+      console.log("临时文件已清理");
+    }
+
+    // 8. 返回错误响应
+    res.status(500).json({
+      success: false,
+      message: errorMessage,
+      error: process.env.NODE_ENV === "development" ? error.stack : undefined,
+    });
+  }
 };

+ 13 - 17
downLoadServer/src/server/controllers/uploadController.js

@@ -1,17 +1,14 @@
 /*
  * @Author: your name
  * @Date: 2025-04-28 14:46:54
- * @LastEditTime: 2025-05-08 15:18:25
+ * @LastEditTime: 2025-05-26 16:19:14
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /downLoadServer/src/server/controllers/uploadController.js
  */
 // src/server/controllers/uploadController.js
 import multer from "multer";
-import {
-  uploadFileToMinIO,
-  checkIfAFileExists,
-} from "../utils/minioService.js";
+import { uploadFileToMinIO } from "../utils/minioService.js";
 import path from "path";
 import fs from "fs-extra";
 import { fileURLToPath } from "url";
@@ -34,17 +31,16 @@ const upload = multer({ storage });
 
 // 上传文件的控制器
 export const uploadFile = async (req, res) => {
-  // const bucketName = req.body.bucketName; // 您的桶名称
-  // const filePath = req.body.filePath; // 从 req.body 中获取文件路径
-  // const objectName = req.body.objectName; // 在 MinIO 中的文件名
-  const bucketName = "bucket-zhzn"; // 您的桶名称
+  const bucketName = req.body.bucketName; // 您的桶名称
   const filePath = req.body.filePath; // 从 req.body 中获取文件路径
-  const objectName = "charts"; // 在 MinIO 中的文件名
+  const objectName = req.body.objectName; // 在 MinIO 中的文件名
   // 检查 filePath 是否有效
   if (typeof filePath !== "string" || !filePath) {
     return res.status(400).json({ message: "Invalid file path" });
   }
   try {
+    // 确保文件存在
+    await fs.access(filePath);
     // 调用 uploadFileToMinIO 函数上传文件
     await uploadFileToMinIO(bucketName, filePath, objectName);
     // 构造文件的 URL
@@ -63,11 +59,11 @@ export const uploadFile = async (req, res) => {
   }
 };
 export const uploadMiddleware = upload.single("file");
-export const checkIfAFileExistsInMinio = async (req, res) => {
-  try {
-    const bucketName = "bucket-zhzn"; // 您的桶名称
-    const objectName = "1111.png"; // 在 MinIO 中的文件名
-    const fileRes = await checkIfAFileExists(bucketName, objectName);
-    console.log(fileRes, "fileRes");
-  } catch (err) {}
+
+export const downloadFile = async (req, res) => {
+  const bucketName = req.body.bucketName;
+  const objectName = req.body.objectName;
+  const filePath = req.body.filePath;
+  const url = `http://${process.env.MINIO_ENDPOINT}:${process.env.MINIO_PORT}/${bucketName}/${objectName}`;
+  res.status(200).json({ url, message: "File downloaded successfully" });
 };

+ 28 - 0
downLoadServer/src/server/routes/chartRoutes.js

@@ -11,6 +11,19 @@ import {
   createColorbarInitTwoDmarkersChart,
   createpowerMarkers2DCharts,
   createRadarCharts,
+  createBoxLineCharts,
+  createBoxMarkersCharts,
+  createGeneratorTemperature,
+  createGetWindRoseChart,
+  createYawErrorLine,
+  createFaultAllCharts,
+  createFaultUnitCharts,
+  createyawErrorCharts,
+  createyawErrorBarSumCharts,
+  createProductionIndicatorTotal,
+  createCopyFileCsv,
+  createPlotlyChartsFen,
+  createPlotlyCharts,
 } from "../controllers/chartController.js";
 
 const router = express.Router();
@@ -28,5 +41,20 @@ router.post(
   createColorbarInitTwoDmarkersChart
 );
 router.post("/powerMarkers2DCharts", createpowerMarkers2DCharts);
+
+router.post("/boxLineCharts", createBoxLineCharts);
+router.post("/boxMarkersCharts", createBoxMarkersCharts);
+router.post("/generatorTemperature", createGeneratorTemperature);
+router.post("/windRoseChart", createGetWindRoseChart);
+router.post("/yawErrorLine", createYawErrorLine);
+router.post("/faultAllChart", createFaultAllCharts);
+router.post("/faultUnitChart", createFaultUnitCharts);
+router.post("/yawErrorChart", createyawErrorCharts);
+router.post("/yawErrorBarSumChart", createyawErrorBarSumCharts);
+router.post("/productionIndicatorTotal", createProductionIndicatorTotal);
 router.post("/radarChart", createRadarCharts);
+router.post("/CopyFileCsv", createCopyFileCsv);
+router.post("/PlotlyChartsFen", createPlotlyChartsFen);
+router.post("/PlotlyCharts", createPlotlyCharts);
+
 export default router;

+ 2 - 2
downLoadServer/src/server/routes/exampleRoutes.js

@@ -3,7 +3,7 @@ import { getExample, createExample } from "../controllers/exampleController.js";
 import {
   uploadFile,
   uploadMiddleware,
-  checkIfAFileExistsInMinio,
+  downloadFile,
 } from "../controllers/uploadController.js"; // 导入上传控制器
 
 const router = express.Router();
@@ -11,5 +11,5 @@ const router = express.Router();
 router.get("/", getExample);
 router.post("/", createExample);
 router.post("/upload", uploadMiddleware, uploadFile); // 添加上传路由
-router.post("/checkIfAFileExistsInMinio", checkIfAFileExistsInMinio);
+router.post("/downLoadCsv", downloadFile);
 export default router;

+ 1 - 1
downLoadServer/src/server/server.js

@@ -17,7 +17,7 @@ const app = express();
 // 使用 cors 中间件
 app.use(cors());
 // 中间件
-app.use(express.json());
+app.use(express.json({ limit: "100mb" }));
 app.use(logger);
 
 // 静态文件服务

+ 15 - 15
downLoadServer/src/server/utils/chartsCom/3DDrawingChart.js

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2025-04-14 16:09:13
- * @LastEditTime: 2025-05-08 15:24:24
+ * @LastEditTime: 2025-05-26 10:55:37
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /downLoadServer/src/server/utils/chartsCom/3DDrawingChart.js
@@ -22,9 +22,8 @@ export const generate3DDrawingChart = async (data, bucketName, objectName) => {
     await fs.ensureDir(tempDir);
     const tempFilePath = path.join(
       tempDir,
-      `temp_heatmap_chart_${Date.now()}.png`
+      `temp_heatmap_chart_${Date.now()}.jpeg`
     );
-
     // 获取 plotly.js 的绝对路径
     const plotlyPath = path.join(
       process.cwd(),
@@ -121,19 +120,20 @@ export const generate3DDrawingChart = async (data, bucketName, objectName) => {
               text: data.zaixs,
             },
           },
+
           aspectratio: {
-            x: 2.2,
-            y: 1.7,
-            z: 1,
+            x: 2.0000000000000018,
+            y: 1.5454545454545465,
+            z: 0.9090909090909098,
           },
           plot_bgcolor: "#e5ecf6",
           gridcolor: "#fff",
           bgcolor: "#e5ecf6", // 设置背景颜色
           camera: {
             up: {
-              x: 0.200292643688136,
-              y: 0.2488259353493132,
-              z: 0.947612004346693,
+              x: 0.19380723218588866,
+              y: 0.2540224158731985,
+              z: 0.9475818534492884,
             },
             center: {
               x: -0.052807476121180814,
@@ -141,9 +141,9 @@ export const generate3DDrawingChart = async (data, bucketName, objectName) => {
               z: -0.022911006648570736,
             },
             eye: {
-              x: -2.126379643342493,
-              y: -2.551422475965373,
-              z: 1.0917667684145647,
+              x: -2.126389777109588,
+              y: -2.5514260394238466,
+              z: 1.091739681861482,
             },
             projection: {
               type: "orthographic",
@@ -170,7 +170,7 @@ export const generate3DDrawingChart = async (data, bucketName, objectName) => {
           <script>${plotlyContent}</script>
         </head>
         <body>
-          <div id="chart" style="width: 100%; height: 450px"></div>
+          <div id="chart" style="width: 100%; height: 500px"></div>
           <script>
             const traces = ${JSON.stringify(traces)};
             const layout = ${JSON.stringify(layout)};
@@ -199,7 +199,7 @@ export const generate3DDrawingChart = async (data, bucketName, objectName) => {
       const chartElement = await page.$("#chart");
       await chartElement.screenshot({
         path: tempFilePath,
-        type: "png",
+        type: "jpeg",
       });
 
       // 上传图片到服务器
@@ -210,7 +210,7 @@ export const generate3DDrawingChart = async (data, bucketName, objectName) => {
         `${process.env.API_BASE_URL}/examples/upload`,
         { filePath: tempFilePath, bucketName, objectName }
       );
-      // return formData;
+      return response?.data?.url;
     } catch (error) {
       console.error("生成3D图失败:", error);
       throw error;

+ 3 - 2
downLoadServer/src/server/utils/chartsCom/BarChart.js

@@ -18,7 +18,7 @@ export const generateBarChart = async (data, bucketName, objectName) => {
     // 创建临时目录
     const tempDir = path.join(process.cwd(), "images");
     await fs.ensureDir(tempDir);
-    const tempFilePath = path.join(tempDir, `temp_chart_${Date.now()}.png`);
+    const tempFilePath = path.join(tempDir, `temp_chart_${Date.now()}.jpeg`);
 
     // 获取 plotly.js 的绝对路径
     const plotlyPath = path.join(
@@ -175,7 +175,7 @@ export const generateBarChart = async (data, bucketName, objectName) => {
       const chartElement = await page.$("#chart");
       await chartElement.screenshot({
         path: tempFilePath,
-        type: "png",
+        type: "jpeg",
       });
 
       // 上传图片到服务器
@@ -206,6 +206,7 @@ export const generateBarChart = async (data, bucketName, objectName) => {
         `${process.env.API_BASE_URL}/examples/upload`,
         { filePath: tempFilePath, bucketName, objectName }
       );
+      return response?.data?.url;
     } finally {
       await browser.close();
     }

+ 238 - 0
downLoadServer/src/server/utils/chartsCom/BoxLineCharts.js

@@ -0,0 +1,238 @@
+/*
+ * @Author: your name
+ * @Date: 2025-05-12 17:40:10
+ * @LastEditTime: 2025-05-21 15:09:53
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /downLoadServer/src/server/utils/chartsCom/BoxLineCharts.js
+ */
+import puppeteer from "puppeteer";
+import fs from "fs-extra";
+import path from "path";
+import FormData from "form-data";
+import { colorSchemes } from "../colors.js";
+import axios from "axios";
+
+/**
+ * 生成柱状图并上传
+ * @param {Object} data - 图表数据
+ * @returns {Promise<String>} - 返回图片URL
+ */
+// ... existing code ...
+
+export const generateBoxLineChart = async (data, bucketName, objectName) => {
+  try {
+    console.log("开始生成图表...");
+    console.log("数据:", data);
+
+    // 创建临时目录
+    const tempDir = path.join(process.cwd(), "images");
+    await fs.ensureDir(tempDir);
+    const tempFilePath = path.join(tempDir, `temp_chart_${Date.now()}.jpeg`);
+
+    // 获取 plotly.js 的绝对路径
+    const plotlyPath = path.join(
+      process.cwd(),
+      "src",
+      "public",
+      "js",
+      "plotly-latest.min.js"
+    );
+    const plotlyContent = await fs.readFile(plotlyPath, "utf-8");
+
+    // 创建浏览器实例
+    const browser = await puppeteer.launch({
+      headless: "new",
+      args: ["--no-sandbox", "--disable-setuid-sandbox"],
+    });
+
+    try {
+      const page = await browser.newPage();
+
+      // 准备图表数据
+      const chartDataset = data.data[0];
+
+      // 过滤数据
+      const filteredData = filterData(chartDataset);
+
+      // 绘制箱线图
+      const trace = {
+        x: filteredData.xData,
+        y: filteredData.yData,
+        type: "box",
+        marker: {
+          color: "#263649",
+        },
+        line: {
+          width: 0.8,
+        },
+        boxpoints: false,
+        boxmean: false,
+        name: filteredData.title,
+        fillcolor: "#458EF7",
+        hovertemplate: `${data.xaixs}: %{x} <br> ${data.yaixs}: %{y} <br>`,
+      };
+
+      let trace2 = {};
+      if (filteredData.medians && filteredData.medians.x.length > 0) {
+        trace2 = {
+          x: filteredData.medians.x,
+          y: filteredData.medians.y,
+          mode: "markers",
+          marker: {
+            color: "#406DAB",
+            size: 3,
+          },
+          name: `${filteredData.title} - 中位点`,
+          type: "scatter",
+        };
+      }
+
+      // 准备布局配置
+      const layout = {
+        title: {
+          text: filteredData.title,
+          font: {
+            size: 16,
+            weight: "bold",
+          },
+        },
+        xaxis: {
+          title: data.xaixs || "X轴",
+          gridcolor: "rgb(255,255,255)",
+          type: data.xaixs === "机组" ? "category" : undefined,
+          tickcolor: "rgb(255,255,255)",
+          backgroundcolor: "#e5ecf6",
+        },
+        yaxis: {
+          title: data.yaixs || "Y轴",
+          gridcolor: "rgb(255,255,255)",
+          tickcolor: "rgb(255,255,255)",
+          backgroundcolor: "#e5ecf6",
+        },
+        showlegend: false,
+        plot_bgcolor: "#e5ecf6",
+        gridcolor: "#fff",
+        bgcolor: "#e5ecf6",
+      };
+
+      // 创建HTML内容
+      const htmlContent = `
+          <!DOCTYPE html>
+          <html>
+            <head>
+              <script>${plotlyContent}</script>
+              <style>
+                body { margin: 0; }
+                #chart { width: 800px; height: 600px; }
+              </style>
+            </head>
+            <body>
+              <div id="chart"></div>
+              <script>
+                window.onload = function() {
+                  const trace = ${JSON.stringify(trace)};
+                  const layout = ${JSON.stringify(layout)};
+                  Plotly.newPlot('chart', [trace, ${JSON.stringify(
+                    trace2
+                  )}], layout).then(() => {
+                    window.chartRendered = true;
+                  });
+                };
+              </script>
+            </body>
+          </html>
+        `;
+
+      // 设置页面内容
+      await page.setContent(htmlContent, {
+        waitUntil: "networkidle0",
+      });
+
+      // 等待图表渲染完成
+      await page.waitForFunction(() => window.chartRendered === true, {
+        timeout: 60000,
+      });
+
+      // 截图并保存到临时文件
+      const chartElement = await page.$("#chart");
+      await chartElement.screenshot({
+        path: tempFilePath,
+        type: "jpeg",
+      });
+
+      // 上传图片到服务器
+      const formData = new FormData();
+      formData.append("file", fs.createReadStream(tempFilePath));
+      const response = await axios.post(
+        `${process.env.API_BASE_URL}/examples/upload`,
+        { filePath: tempFilePath, bucketName, objectName }
+      );
+      return response?.data?.url;
+    } finally {
+      await browser.close();
+    }
+  } catch (error) {
+    console.error("生成图表失败:", error);
+    throw error;
+  }
+};
+
+// 过滤数据的函数
+const filterData = (group) => {
+  const filteredXData = [];
+  const filteredYData = [];
+  const filteredMedians = group.medians ? { x: [], y: [] } : null;
+
+  if (isDateType(group.xData)) {
+    group.xData.forEach((timestamp, index) => {
+      if (isInDateRange(timestamp)) {
+        filteredXData.push(timestamp);
+        filteredYData.push(group.yData[index]);
+
+        if (filteredMedians && isInDateRange(group.medians.x[index])) {
+          filteredMedians.x.push(group.medians.x[index]);
+          filteredMedians.y.push(group.medians.y[index]);
+        }
+      }
+    });
+  } else {
+    filteredXData.push(...group.xData);
+    filteredYData.push(...group.yData);
+
+    if (group.medians) {
+      filteredMedians.x.push(...group.medians.x);
+      filteredMedians.y.push(...group.medians.y);
+    }
+  }
+
+  return {
+    ...group,
+    xData: filteredXData,
+    yData: filteredYData,
+    medians: filteredMedians,
+  };
+};
+
+// 判断是否为日期类型的函数
+const isDateType = (xData) => {
+  // 实现判断逻辑
+  if (xData) {
+    const firstTimestamp = xData[0];
+    return !isNaN(Date.parse(firstTimestamp)); // 判断是否是有效日期
+  } else {
+    return false;
+  }
+};
+
+// 判断是否在日期范围内的函数
+const isInDateRange = (timestamp) => {
+  // 实现判断逻辑
+  const [startDate, endDate] = [];
+  if (!startDate || !endDate) return true;
+
+  const date = new Date(timestamp);
+  return date >= new Date(startDate) && date <= new Date(endDate);
+};
+
+// ... existing code ...

+ 206 - 0
downLoadServer/src/server/utils/chartsCom/BoxMarkersCharts.js

@@ -0,0 +1,206 @@
+/*
+ * @Author: your name
+ * @Date: 2025-05-13 11:02:32
+ * @LastEditTime: 2025-05-26 10:57:41
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /downLoadServer/src/server/utils/chartsCom/BoxMarkersCharts.js
+ */
+import puppeteer from "puppeteer";
+import fs from "fs-extra";
+import path from "path";
+import FormData from "form-data";
+import { colorSchemes } from "../colors.js";
+import axios from "axios";
+
+// ... existing code ...
+// 判断时间戳是否在选择的日期范围内
+const isInDateRange = (timestamp) => {
+  const [startDate, endDate] = [];
+  if (!startDate || !endDate) return true;
+
+  const date = new Date(timestamp);
+  return date >= new Date(startDate) && date <= new Date(endDate);
+};
+
+// 过滤数据
+const filterData = (group) => {
+  const filteredXData = [];
+  const filteredYData = [];
+  const filteredMedians = group.medians ? { x: [], y: [] } : null;
+
+  group.medians.x.forEach((timestamp, index) => {
+    if (isInDateRange(timestamp)) {
+      filteredMedians.x.push(timestamp);
+      filteredMedians.y.push(group.medians.y[index]);
+    }
+  });
+
+  group.xData.forEach((timestamp, index) => {
+    if (isInDateRange(timestamp)) {
+      filteredXData.push(timestamp);
+      filteredYData.push(group.yData[index]);
+    }
+  });
+  console.log(filteredMedians, "filteredMedians h过滤中心值");
+  return {
+    ...group,
+    xData: filteredXData,
+    yData: filteredYData,
+    medians: filteredMedians,
+  };
+};
+
+// 绘制箱线图
+// ... existing code ...
+
+// 绘制箱线图
+export const generateBoxMarkersCharts = async (
+  data,
+  bucketName,
+  objectName
+) => {
+  const { data: chartData, xaixs, yaixs, analysisTypeCode } = data;
+
+  const filteredData = chartData.map((group) => filterData(group));
+
+  const traces = [];
+  const medianMarkers = [];
+  // 获取 plotly.js 的绝对路径
+  const plotlyPath = path.join(
+    process.cwd(),
+    "src",
+    "public",
+    "js",
+    "plotly-3.0.1.min.js"
+  );
+  const plotlyContent = await fs.readFile(plotlyPath, "utf-8");
+
+  filteredData.forEach((group) => {
+    traces.push({
+      x: group.xData,
+      y: group.yData,
+      type: "box",
+      name: group.title,
+      marker: { color: "#263649" },
+      line: { width: 0.8 },
+      fillcolor: "#458EF7",
+      boxpoints: false,
+      boxmean: true,
+      hovertemplate:
+        `<b>${xaixs}</b>: %{x}<br>` +
+        `<b>最大值</b>: %{upperfence}<br>` +
+        `<b>上四分位数 (Q3)</b>: %{q3}<br>` +
+        `<b>中位数 (Median)</b>: %{median}<br>` +
+        `<b>下四分位数 (Q1)</b>: %{q1}<br>` +
+        `<b>最小值</b>: %{lowerfence}<br>` +
+        `<b>均值</b>: %{mean}<br>` +
+        `<extra></extra>`,
+    });
+
+    if (group.medians && group.medians.x.length > 0) {
+      medianMarkers.push({
+        x: group.medians.x,
+        y: group.medians.y,
+        mode: "markers",
+        marker: { color: "#f00", size: 3 },
+        name: `${group.title} - 中位点`,
+        type: "scatter",
+        hovertemplate: `<b>${xaixs}</b>: %{x} <br> <b>${yaixs}</b>: %{y} <br><b>中位点</b>: %{y}<br><extra></extra>`,
+      });
+    }
+  });
+
+  const layout = {
+    title: {
+      text: analysisTypeCode + chartData[0].engineName,
+      font: { size: 16, weight: "bold" },
+    },
+    xaxis: {
+      title: xaixs,
+      type: "date",
+      tickformat: "%Y-%m-%d",
+      gridcolor: "rgb(255,255,255)",
+      tickcolor: "rgb(255,255,255)",
+      backgroundcolor: "#e5ecf6",
+    },
+    yaxis: {
+      title: yaixs,
+      gridcolor: "rgb(255,255,255)",
+      tickcolor: "rgb(255,255,255)",
+      backgroundcolor: "#e5ecf6",
+    },
+    plot_bgcolor: "#e5ecf6",
+    showlegend: true,
+  };
+
+  // 使用 Puppeteer 生成图表的截图
+  const browser = await puppeteer.launch({
+    headless: true,
+    args: ["--no-sandbox", "--disable-setuid-sandbox"],
+  });
+
+  try {
+    const page = await browser.newPage();
+    const htmlContent = `
+        <!DOCTYPE html>
+        <html>
+          <head>
+           <meta charset="UTF-8">
+           <script>${plotlyContent}</script>
+            <style>
+              body { margin: 0; }
+              #chart { width: 800px; height: 600px; }
+            </style>
+          </head>
+          <body>
+            <div id="chart"></div>
+            <script>
+              window.onload = function() {
+                const traces = ${JSON.stringify([...traces, ...medianMarkers])};
+                const layout = ${JSON.stringify(layout)};
+                Plotly.newPlot('chart', traces, layout).then(() => {
+                  window.chartRendered = true;
+                });
+              };
+            </script>
+          </body>
+        </html>
+      `;
+
+    await page.setContent(htmlContent, { waitUntil: "networkidle0" });
+    await page.waitForFunction(() => window.chartRendered === true, {
+      timeout: 60000,
+    });
+
+    // 截图并保存到临时文件
+    const tempFilePath = path.join(
+      process.cwd(),
+      "images",
+      `chart_${Date.now()}.jpeg`
+    );
+    const chartElement = await page.$("#chart");
+    await chartElement.screenshot({ path: tempFilePath, type: "jpeg" });
+
+    // 上传图片到服务器
+    const formData = new FormData();
+    formData.append("file", fs.createReadStream(tempFilePath));
+    const response = await axios.post(
+      `${process.env.API_BASE_URL}/examples/upload`,
+      {
+        filePath: tempFilePath,
+        bucketName,
+        objectName,
+      }
+    );
+
+    console.log("上传成功:", response.data);
+    return response?.data?.url;
+  } finally {
+    await browser.close();
+  }
+};
+
+// ... existing code ...
+
+// ... existing code ...

+ 4 - 3
downLoadServer/src/server/utils/chartsCom/ColorbarInitTwoDmarkersChart.js

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2025-04-28 10:27:00
- * @LastEditTime: 2025-05-08 15:24:44
+ * @LastEditTime: 2025-05-26 10:58:01
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /downLoadServer/src/server/utils/chartsCom/ColorbarInitTwoDmarkersChart.js
@@ -25,7 +25,7 @@ export const generateColorbarInitTwoDmarkersChart = async (
     await fs.ensureDir(tempDir);
     const tempFilePath = path.join(
       tempDir,
-      `temp_scatter_chart_${Date.now()}.png`
+      `temp_scatter_chart_${Date.now()}.jpeg`
     );
 
     // 获取 plotly.js 的绝对路径
@@ -196,7 +196,7 @@ export const generateColorbarInitTwoDmarkersChart = async (
       const chartElement = await page.$("#chart");
       await chartElement.screenshot({
         path: tempFilePath,
-        type: "png",
+        type: "jpeg",
       });
 
       // 上传图片到服务器
@@ -208,6 +208,7 @@ export const generateColorbarInitTwoDmarkersChart = async (
         `${process.env.API_BASE_URL}/examples/upload`,
         { filePath: tempFilePath, bucketName, objectName }
       );
+      return response?.data?.url;
     } catch (error) {
       console.error("生成2D散点图失败:", error);
       throw error;

+ 181 - 0
downLoadServer/src/server/utils/chartsCom/FaultAll.js

@@ -0,0 +1,181 @@
+import puppeteer from "puppeteer";
+import fs from "fs-extra";
+import path from "path";
+import FormData from "form-data";
+import axios from "axios"; // 导入 axios
+import { colorSchemes } from "../colors.js";
+
+export const getFaultAllCharts = async (
+  data,
+  bucketName,
+  objectName,
+  analysisTypeCode
+) => {
+  // 获取 plotly.js 的绝对路径
+  const plotlyPath = path.join(
+    process.cwd(),
+    "src",
+    "public",
+    "js",
+    "plotly-3.0.1.min.js"
+  );
+  const plotlyContent = await fs.readFile(plotlyPath, "utf-8");
+  console.log(data, "全场故障数据");
+  try {
+    // 提取故障类型、故障次数和故障时长
+    const faultTypes = data.map((item) => item.fault_detail);
+    const faultCounts = data.map((item) => item.count);
+    const faultDurations = data.map((item) => item.fault_time_sum);
+
+    // 故障次数的柱状图数据(左侧 Y 轴)
+    const barTrace = {
+      x: faultTypes.slice(0, 10),
+      y: faultCounts.slice(0, 10),
+      type: "bar",
+      marker: { color: "#64ADC2" }, // 蓝色柱状图
+      name: "故障次数",
+      hovertemplate: `故障类型: %{x} <br> 故障次数: %{y} 次<br>`,
+    };
+
+    // 故障时长的折线图数据(右侧 Y 轴)
+    const lineTrace = {
+      x: faultTypes.slice(0, 10),
+      y: faultDurations.slice(0, 10),
+      type: "scatter",
+      mode: "lines+markers", // 线性图 + 点标记
+      line: { color: "#1A295D" }, // 红色折线
+      name: "故障时长",
+      yaxis: "y2", // 使用第二个 Y 轴(右侧)
+      hovertemplate: `故障类型: %{x} <br> 故障时长: %{y} 分钟 <br>`,
+    };
+
+    // 布局配置,设置双 Y 轴
+    const layout = {
+      title: {
+        text: "全场故障次数与时长分析Top10",
+        font: {
+          size: 16, // 设置标题字体大小(默认 16)
+          weight: "bold",
+        },
+      },
+      xaxis: {
+        title: {
+          text: "故障类型",
+        },
+
+        tickangle: 30,
+        tickmode: "array",
+        tickvals: faultTypes.slice(0, 10),
+        tickfont: { size: 12 },
+        gridcolor: "rgb(255,255,255)",
+        tickcolor: "rgb(255,255,255)",
+        backgroundcolor: "#e5ecf6",
+      },
+      yaxis: {
+        title: {
+          text: "故障次数",
+        },
+        titlefont: { color: "#64ADC2" },
+        tickfont: { color: "#64ADC2" },
+        side: "left", // 左侧的 Y 轴
+        showline: true,
+        linecolor: "#64ADC2",
+        gridcolor: "rgb(255,255,255)",
+        tickcolor: "rgb(255,255,255)",
+        backgroundcolor: "#e5ecf6",
+      },
+      yaxis2: {
+        title: {
+          text: "故障时长 (分钟)",
+        },
+        titlefont: { color: "#1A295D" },
+        tickfont: { color: "#1A295D" },
+        overlaying: "y", // 在第一个 Y 轴上方绘制
+        side: "right", // 右侧的 Y 轴
+        position: 1, // 调整右侧轴的位置
+        showline: true,
+        linecolor: "#1A295D", // 设置右侧轴线颜色
+      },
+      barmode: "group", // 柱状图分组
+      plot_bgcolor: "#e5ecf6",
+      gridcolor: "#fff",
+      bgcolor: "#e5ecf6", // 设置背景颜色
+      showlegend: false,
+      margin: {
+        t: 80, // 上边距
+        b: 150, // 下边距,给 X 轴标签更多空间
+      },
+    };
+
+    // 创建临时目录
+    const tempDir = path.join(process.cwd(), "images");
+    await fs.ensureDir(tempDir);
+    const tempFilePath = path.join(
+      tempDir,
+      `temp_fault_all_chart_${Date.now()}.jpeg`
+    );
+
+    // 使用 Puppeteer 生成图表的截图
+    const browser = await puppeteer.launch({
+      headless: "new",
+      args: ["--no-sandbox", "--disable-setuid-sandbox"],
+    });
+    try {
+      const page = await browser.newPage();
+      const htmlContent = `
+        <!DOCTYPE html>
+        <html>
+          <head>
+            <meta charset="UTF-8">
+            <title>全场故障</title>
+            <script>${plotlyContent}</script>
+            <style>
+              body { margin: 0; }
+              #chart { width: 800px; height: 600px; }
+            </style>
+          </head>
+          <body>
+            <div id="chart"></div>
+            <script>
+              window.onload = function() {
+                Plotly.newPlot('chart', [${JSON.stringify(
+                  barTrace
+                )}, ${JSON.stringify(lineTrace)}], ${JSON.stringify(
+        layout
+      )}).then(() => {
+                  window.chartRendered = true;
+                });
+              };
+            </script>
+          </body>
+        </html>
+      `;
+      await page.setContent(htmlContent, { waitUntil: "networkidle0" });
+      await page.waitForFunction(() => window.chartRendered === true, {
+        timeout: 60000,
+      });
+      // 截图并保存到临时文件
+      const chartElement = await page.$("#chart");
+      await chartElement.screenshot({ path: tempFilePath, type: "jpeg" });
+      // 上传图片到服务器
+      const formData = new FormData();
+      formData.append("file", fs.createReadStream(tempFilePath));
+      const response = await axios.post(
+        `${process.env.API_BASE_URL}/examples/upload`,
+        {
+          filePath: tempFilePath,
+          bucketName,
+          objectName,
+        }
+      );
+      console.log("上传成功:", response.data);
+      return response?.data?.url;
+    } catch (error) {
+      console.error("生成图表失败:", error);
+    } finally {
+      await browser.close();
+    }
+  } catch (error) {
+    console.error("发生错误:", error);
+  }
+};

+ 181 - 0
downLoadServer/src/server/utils/chartsCom/FaultUnit.js

@@ -0,0 +1,181 @@
+import puppeteer from "puppeteer";
+import fs from "fs-extra";
+import path from "path";
+import FormData from "form-data";
+import axios from "axios"; // 导入 axios
+import { colorSchemes } from "../colors.js";
+
+export const getFaultUnitCharts = async (
+  data,
+  bucketName,
+  objectName,
+  analysisTypeCode
+) => {
+  try {
+    // 获取 plotly.js 的绝对路径
+    const plotlyPath = path.join(
+      process.cwd(),
+      "src",
+      "public",
+      "js",
+      "plotly-3.0.1.min.js"
+    );
+    const plotlyContent = await fs.readFile(plotlyPath, "utf-8");
+
+    // 提取故障机组、故障次数和故障时长
+    const faultTypes = data.map((item) => item.wind_turbine_name);
+    const faultCounts = data.map((item) => item.count);
+    const faultDurations = data.map((item) => item.fault_time);
+    console.log();
+    // 故障次数的散点图数据(左侧 Y 轴)
+    const scatterFaultCounts = {
+      x: faultTypes.slice(0, 10),
+      y: faultCounts.slice(0, 10),
+      mode: "markers", // 散点图
+      marker: { color: "#64ADC2", size: 10 }, // 蓝色散点
+      name: "故障次数",
+      hovertemplate: `机组: %{x} <br> 故障次数: %{y} 次<br>`,
+    };
+
+    // 故障时长的散点图数据(右侧 Y 轴)
+    const scatterFaultDurations = {
+      x: faultTypes.slice(0, 10),
+      y: faultDurations.slice(0, 10),
+      mode: "markers", // 散点图
+      marker: { color: "#1A295D", size: 10 }, // 红色散点
+      name: "故障时长",
+      yaxis: "y2", // 使用第二个 Y 轴(右侧)
+      hovertemplate: `机组: %{x} <br> 故障时长: %{y} 秒<br>`,
+    };
+
+    // 布局配置,设置双 Y 轴
+    const layout = {
+      title: {
+        text: "机组故障次数与时长分析Top10",
+        font: {
+          size: 16,
+          weight: "bold",
+        },
+      },
+      xaxis: {
+        title: {
+          text: "故障机组",
+        },
+        tickangle: 30,
+        tickmode: "array",
+        tickvals: faultTypes.slice(0, 10), // 保证这里是字符串数组
+        ticktext: faultTypes.slice(0, 10), // 确保 ticktext 使用相同的标签
+        tickfont: { size: 12 },
+        type: "category", // 让 Y 轴按类别均匀分布
+        gridcolor: "rgb(255,255,255)",
+        tickcolor: "rgb(255,255,255)",
+        backgroundcolor: "#e5ecf6",
+      },
+      yaxis: {
+        title: {
+          text: "故障次数",
+        },
+        titlefont: { color: "#64ADC2" },
+        tickfont: { color: "#64ADC2" },
+        side: "left", // 左侧的 Y 轴
+        showline: true,
+        linecolor: "#64ADC2",
+        gridcolor: "rgb(255,255,255)",
+        tickcolor: "rgb(255,255,255)",
+        backgroundcolor: "#e5ecf6",
+      },
+      yaxis2: {
+        title: {
+          text: "故障时长(秒)",
+        },
+        titlefont: { color: "#1A295D" },
+        tickfont: { color: "#1A295D" },
+        showgrid: false, // 隐藏 Y 轴网格线
+        overlaying: "y", // 在第一个 Y 轴上方绘制
+        side: "right", // 右侧的 Y 轴
+        position: 1, // 调整右侧轴的位置
+        showline: true,
+        linecolor: "#1A295D", // 设置右侧轴线颜色
+      },
+      plot_bgcolor: "#e5ecf6",
+      gridcolor: "#fff",
+      bgcolor: "#e5ecf6", // 设置背景颜色
+      showlegend: false, // 显示图例
+      margin: {
+        t: 80, // 上边距
+        b: 150, // 下边距,给 X 轴标签更多空间
+      },
+    };
+
+    // 创建临时目录
+    const tempDir = path.join(process.cwd(), "images");
+    await fs.ensureDir(tempDir);
+    const tempFilePath = path.join(
+      tempDir,
+      `temp_fault_unit_chart_${Date.now()}.jpeg`
+    );
+
+    // 使用 Puppeteer 生成图表的截图
+    const browser = await puppeteer.launch({
+      headless: "new",
+      args: ["--no-sandbox", "--disable-setuid-sandbox"],
+    });
+    try {
+      const page = await browser.newPage();
+      const htmlContent = `
+        <!DOCTYPE html>
+        <html>
+          <head>
+            <meta charset="UTF-8">
+            <title>机组故障</title>
+            <script>${plotlyContent}</script>
+            <style>
+              body { margin: 0; }
+              #chart { width: 800px; height: 600px; }
+            </style>
+          </head>
+          <body>
+            <div id="chart"></div>
+            <script>
+              window.onload = function() {
+                Plotly.newPlot('chart', [${JSON.stringify(
+                  scatterFaultCounts
+                )}, ${JSON.stringify(scatterFaultDurations)}], ${JSON.stringify(
+        layout
+      )}).then(() => {
+                  window.chartRendered = true;
+                });
+              };
+            </script>
+          </body>
+        </html>
+      `;
+      await page.setContent(htmlContent, { waitUntil: "networkidle0" });
+      await page.waitForFunction(() => window.chartRendered === true, {
+        timeout: 60000,
+      });
+      // 截图并保存到临时文件
+      const chartElement = await page.$("#chart");
+      await chartElement.screenshot({ path: tempFilePath, type: "jpeg" });
+      // 上传图片到服务器
+      const formData = new FormData();
+      formData.append("file", fs.createReadStream(tempFilePath));
+      const response = await axios.post(
+        `${process.env.API_BASE_URL}/examples/upload`,
+        {
+          filePath: tempFilePath,
+          bucketName,
+          objectName,
+        }
+      );
+      // console.log("上传成功:", response.data);
+      return response?.data?.url;
+    } catch (error) {
+      console.error("生成图表失败:", error);
+    } finally {
+      await browser.close();
+    }
+  } catch (error) {
+    console.error("发生错误:", error);
+  }
+};

+ 183 - 0
downLoadServer/src/server/utils/chartsCom/GeneratorTemperature.js

@@ -0,0 +1,183 @@
+/*
+ * @Author: your name
+ * @Date: 2025-05-13 14:33:50
+ * @LastEditTime: 2025-05-21 15:10:12
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /downLoadServer/src/server/utils/chartsCom/GeneratorTemperature.js
+ */
+import puppeteer from "puppeteer";
+import fs from "fs-extra";
+import path from "path";
+import FormData from "form-data";
+import { colorSchemes } from "../colors.js";
+import axios from "axios";
+// ... existing code ...
+
+export const generateGeneratorTemperature = async (
+  data,
+  bucketName,
+  objectName
+) => {
+  try {
+    console.log("开始生成图表...");
+    console.log("数据:", data);
+    const typeLine = [
+      "solid",
+      "dot",
+      "dash",
+      "longdash",
+      "dashdot",
+      "longdashdot",
+    ];
+    // 创建临时目录
+    const tempDir = path.join(process.cwd(), "images");
+    await fs.ensureDir(tempDir);
+    const tempFilePath = path.join(tempDir, `temp_chart_${Date.now()}.jpeg`);
+
+    // 获取 plotly.js 的绝对路径
+    const plotlyPath = path.join(
+      process.cwd(),
+      "src",
+      "public",
+      "js",
+      "plotly-latest.min.js"
+    );
+    const plotlyContent = await fs.readFile(plotlyPath, "utf-8");
+    const colorDefault = colorSchemes[0].colors;
+    // 创建浏览器实例
+    const browser = await puppeteer.launch({
+      headless: "new",
+      args: ["--no-sandbox", "--disable-setuid-sandbox"],
+    });
+
+    try {
+      const page = await browser.newPage();
+
+      // 准备图表数据
+      const chartDataset = data.data;
+      const chartData = []; // 用于存储图表配置
+
+      chartDataset.forEach((turbine, index) => {
+        const chartConfig = {
+          x: turbine.xData,
+          y: turbine.yData,
+          name: turbine.Name,
+          line: {
+            dash: typeLine[index % colorDefault.length],
+            color:
+              data.color1 && data.color1.length > 0
+                ? data.color1[index % data.color1.length]
+                : colorDefault[index % colorDefault.length],
+          },
+          marker: {
+            color:
+              data.color1 && data.color1.length > 0
+                ? data.color1[index % data.color1.length]
+                : colorDefault[index % colorDefault.length],
+          },
+          hovertemplate: `${data.xaixs}: %{x} <br> ${data.yaixs}: %{y} <br>`,
+        };
+
+        if (data.chartType === "line") {
+          chartConfig.fill = "none";
+          chartConfig.mode = "lines";
+        } else if (data.chartType === "bar") {
+          chartConfig.fill = "tonexty";
+        }
+
+        chartData.push(chartConfig);
+      });
+
+      const layout = {
+        title: {
+          text: data.title,
+          font: {
+            size: 16,
+            weight: "bold",
+          },
+        },
+        xaxis: {
+          title: data.xaixs || "X轴",
+          gridcolor: "rgb(255,255,255)",
+          tickcolor: "rgb(255,255,255)",
+          backgroundcolor: "#e5ecf6",
+        },
+        yaxis: {
+          title: data.yaixs || "Y轴",
+          gridcolor: "rgb(255,255,255)",
+          tickcolor: "rgb(255,255,255)",
+          backgroundcolor: "#e5ecf6",
+        },
+        margin: {
+          l: 50,
+          r: 50,
+          t: 50,
+          b: 50,
+        },
+        plot_bgcolor: "#e5ecf6",
+        gridcolor: "#fff",
+        bgcolor: "#e5ecf6",
+        autosize: true,
+        barmode: data.chartType === "bar" ? "stack" : "group",
+      };
+
+      // 创建HTML内容
+      const htmlContent = `
+            <!DOCTYPE html>
+            <html>
+              <head>
+                <script>${plotlyContent}</script>
+                <style>
+                  body { margin: 0; }
+                  #chart { width: 800px; height: 600px; }
+                </style>
+              </head>
+              <body>
+                <div id="chart"></div>
+                <script>
+                  window.onload = function() {
+                    Plotly.newPlot('chart', ${JSON.stringify(
+                      chartData
+                    )}, ${JSON.stringify(layout)}).then(() => {
+                      window.chartRendered = true;
+                    });
+                  };
+                </script>
+              </body>
+            </html>
+          `;
+
+      // 设置页面内容
+      await page.setContent(htmlContent, {
+        waitUntil: "networkidle0",
+      });
+
+      // 等待图表渲染完成
+      await page.waitForFunction(() => window.chartRendered === true, {
+        timeout: 60000,
+      });
+
+      // 截图并保存到临时文件
+      const chartElement = await page.$("#chart");
+      await chartElement.screenshot({
+        path: tempFilePath,
+        type: "jpeg",
+      });
+
+      // 上传图片到服务器
+      const formData = new FormData();
+      formData.append("file", fs.createReadStream(tempFilePath));
+      const response = await axios.post(
+        `${process.env.API_BASE_URL}/examples/upload`,
+        { filePath: tempFilePath, bucketName, objectName }
+      );
+      return response?.data?.url;
+    } finally {
+      await browser.close();
+    }
+  } catch (error) {
+    console.error("生成图表失败:", error);
+    throw error;
+  }
+};

+ 4 - 3
downLoadServer/src/server/utils/chartsCom/HeatmapCharts.js

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2025-04-14 11:15:35
- * @LastEditTime: 2025-05-08 15:25:27
+ * @LastEditTime: 2025-05-21 15:10:19
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/downLoadServer/src/server/utils/chartsCom/HeatmapCharts.js
@@ -23,7 +23,7 @@ export const generateHeatmapChart = async (data, bucketName, objectName) => {
     await fs.ensureDir(tempDir);
     const tempFilePath = path.join(
       tempDir,
-      `temp_heatmap_chart_${Date.now()}.png`
+      `temp_heatmap_chart_${Date.now()}.jpeg`
     );
 
     // 获取 plotly.js 的绝对路径
@@ -142,7 +142,7 @@ export const generateHeatmapChart = async (data, bucketName, objectName) => {
       const chartElement = await page.$("#chart");
       await chartElement.screenshot({
         path: tempFilePath,
-        type: "png",
+        type: "jpeg",
       });
 
       // 上传图片到服务器
@@ -166,6 +166,7 @@ export const generateHeatmapChart = async (data, bucketName, objectName) => {
         `${process.env.API_BASE_URL}/examples/upload`,
         { filePath: tempFilePath, bucketName, objectName }
       );
+      return response?.data?.url;
     } catch (error) {
       console.error("生成热力图失败:", error);
       throw error;

+ 183 - 0
downLoadServer/src/server/utils/chartsCom/PlotlyCharts.js

@@ -0,0 +1,183 @@
+/*
+ * @Author: your name
+ * @Date: 2025-05-15 15:22:19
+ * @LastEditTime: 2025-05-26 11:00:41
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /downLoadServer/src/server/utils/chartsCom/PlotlyCharts.js
+ */
+
+import puppeteer from "puppeteer";
+import fs from "fs-extra";
+import path from "path";
+import FormData from "form-data";
+import { colorSchemes } from "../colors.js";
+import axios from "axios";
+import { text } from "stream/consumers";
+
+/**
+ * 生成折线图并上传
+ * @param {Object} options - 配置选项
+ * @param {string} options.fileAddr - 数据文件地址
+ * @param {string[]} options.color1 - 颜色数组1
+ * @param {string[]} options.colors - 颜色数组2
+ * @returns {Promise<String>} - 返回图片URL
+ */
+export const generatePlotlyCharts = async (
+  chartData,
+  bucketName,
+  objectName
+) => {
+  const colorSchemesItem = colorSchemes[0].colors;
+  const tempDir = path.join(process.cwd(), "images");
+  const tempFilePath = path.join(tempDir, `temp_line_chart_${Date.now()}.jpeg`);
+  let browser;
+
+  try {
+    // 构建数据
+    const data = [];
+    const newData = chartData.data.filter(
+      (item) => item.enginName !== "合同功率曲线"
+    );
+
+    newData.forEach((turbine, index) => {
+      data.push({
+        x: turbine.xData,
+        y: turbine.yData,
+        name: turbine.enginName,
+        mode: "lines",
+        connectgaps: false,
+        line: { color: colorSchemesItem[index % colorSchemesItem.length] },
+        marker: { color: colorSchemesItem[index % colorSchemesItem.length] },
+      });
+    });
+
+    if (
+      chartData.data[chartData.data.length - 1] &&
+      chartData.data[chartData.data.length - 1].enginName === "合同功率曲线" &&
+      chartData.data[chartData.data.length - 1].yData.length > 0
+    ) {
+      data.push({
+        x: chartData.data[chartData.data.length - 1].xData,
+        y: chartData.data[chartData.data.length - 1].yData,
+        mode: "lines+markers",
+        name: "合同功率曲线",
+        line: {
+          color: "red",
+          width: 1,
+        },
+        marker: { color: "red", size: 4 },
+      });
+    }
+
+    const layout = {
+      title: {
+        text: `有功功率曲线分析${chartData.engineTypeName}`,
+        font: { size: 16, weight: "bold" },
+      },
+      xaxis: { title: { text: "风速(m / s)" || "X轴" }, gridcolor: "#fff" },
+      yaxis: { title: { text: "功率(kW)" || "Y轴" }, gridcolor: "#fff" },
+      margin: { l: 50, r: 50, t: 50, b: 50 },
+      plot_bgcolor: "#e5ecf6",
+      bgcolor: "#e5ecf6",
+      autosize: true,
+      barmode: "group",
+      legend: {
+        orientation: "h",
+        xanchor: "center",
+        x: 0.5,
+        y: -0.2,
+      },
+    };
+
+    await fs.ensureDir(tempDir);
+    const plotlyPath = path.join(
+      process.cwd(),
+      "src",
+      "public",
+      "js",
+      "plotly-latest.min.js"
+    );
+    const plotlyContent = await fs.readFile(plotlyPath, "utf-8");
+
+    browser = await puppeteer.launch({
+      headless: "new",
+      args: ["--no-sandbox", "--disable-setuid-sandbox"],
+    });
+
+    const page = await browser.newPage();
+
+    const htmlContent = `
+      <!DOCTYPE html>
+      <html>
+        <head>
+          <script>${plotlyContent}</script>
+          <style>body { margin: 0; } #chart { width: 800px; height: 600px; }</style>
+        </head>
+        <body>
+          <div id="chart"></div>
+          <script>
+            window.onload = function () {
+              const data = ${JSON.stringify(data)};
+              const layout = ${JSON.stringify(layout)};
+              Plotly.newPlot('chart', data, layout, { responsive: true }).then(() => {
+                window.chartRendered = true;
+              });
+            };
+          </script>
+        </body>
+      </html>`;
+
+    await page.setContent(htmlContent, { waitUntil: "networkidle0" });
+
+    await page.waitForFunction(() => window.chartRendered === true, {
+      timeout: 60000,
+    });
+
+    const chartElement = await page.$("#chart");
+    await chartElement.screenshot({ path: tempFilePath, type: "jpeg" });
+
+    // ✅ 上传前判断文件是否存在
+    if (!(await fs.pathExists(tempFilePath))) {
+      console.error("❌ 图表文件未生成:", tempFilePath);
+      throw new Error("图表截图文件未生成");
+    }
+
+    // ✅ 发起上传
+    try {
+      const response = await axios.post(
+        `${process.env.API_BASE_URL}/examples/upload`,
+        {
+          filePath: tempFilePath,
+          bucketName,
+          objectName,
+        }
+      );
+
+      const imageUrl = response?.data?.url;
+      console.log("✅ 上传成功:", imageUrl);
+
+      return imageUrl;
+    } catch (uploadError) {
+      console.error("❌ 上传失败:", uploadError.message);
+      throw uploadError;
+    } finally {
+      // ✅ 上传后安全删除
+      try {
+        if (await fs.pathExists(tempFilePath)) {
+          await fs.unlink(tempFilePath);
+          console.log("🧹 临时文件已删除:", tempFilePath);
+        }
+      } catch (deleteError) {
+        console.warn("⚠️ 删除临时文件失败:", deleteError.message);
+      }
+    }
+  } catch (error) {
+    console.error("❌ 生成折线图失败:", error.message);
+    throw error;
+  } finally {
+    if (browser) {
+      await browser.close();
+    }
+  }
+};

+ 217 - 0
downLoadServer/src/server/utils/chartsCom/PlotlyChartsFen.js

@@ -0,0 +1,217 @@
+/*
+ * @Author: your name
+ * @Date: 2025-05-23 17:19:20
+ * @LastEditTime: 2025-05-26 11:36:31
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /downLoadServer/src/server/utils/chartsCom/PlotlyChartsFen.js
+ */
+import puppeteer from "puppeteer";
+import fs from "fs-extra";
+import path from "path";
+import FormData from "form-data";
+import { colorSchemes } from "../colors.js";
+import axios from "axios";
+
+/**
+ * 生成折线图并上传
+ * @param {Object} data - 图表数据
+ * @returns {Promise<String>} - 返回图片URL
+ */
+export const generatePlotlyChartsFen = async (
+  data,
+  bucketName,
+  objectName,
+  fieldEngineCode
+) => {
+  try {
+    // 创建临时目录
+    const tempDir = path.join(process.cwd(), "images");
+    await fs.ensureDir(tempDir);
+    const tempFilePath = path.join(
+      tempDir,
+      `temp_heatmap_chart_${Date.now()}.jpeg`
+    );
+
+    // 获取 plotly.js 的绝对路径
+    const plotlyPath = path.join(
+      process.cwd(),
+      "src",
+      "public",
+      "js",
+      "plotly-latest.min.js"
+    );
+    const plotlyContent = await fs.readFile(plotlyPath, "utf-8");
+
+    // 创建浏览器实例
+    const browser = await puppeteer.launch({
+      headless: "new",
+      args: ["--no-sandbox", "--disable-setuid-sandbox"],
+    });
+
+    try {
+      const page = await browser.newPage();
+
+      // 准备图表数据
+      const sortedData = data.data.sort((a, b) => {
+        if (
+          a.enginCode === fieldEngineCode &&
+          b.enginCode !== fieldEngineCode
+        ) {
+          return 1;
+        }
+        if (
+          a.enginCode !== fieldEngineCode &&
+          b.enginCode === fieldEngineCode
+        ) {
+          return -1;
+        }
+        return 0;
+      });
+
+      const finalData = [];
+      let enginName = "";
+      console.log(fieldEngineCode, "fieldEngineCode");
+      sortedData
+        .filter((item) => item.enginName !== "合同功率曲线")
+        .forEach((turbine) => {
+          const color =
+            turbine.enginCode === fieldEngineCode ? "#406DAB" : "#D3D3D3";
+          enginName =
+            turbine.enginCode === fieldEngineCode ? turbine.enginName : "";
+          const chartConfig = {
+            x: turbine.xData,
+            y: turbine.yData,
+            connectgaps: false,
+            name: turbine.enginName,
+            mode: "lines",
+            fill: "none",
+            line: { color },
+            marker: { color },
+          };
+          finalData.push(chartConfig);
+        });
+      const filterData = data.data.filter(
+        (item) => item.enginName === "合同功率曲线"
+      );
+      console.log(filterData, "合同功率曲线");
+      // 添加合同功率曲线
+      if (filterData && filterData[0].enginName === "合同功率曲线") {
+        finalData.push({
+          x: filterData[0].xData,
+          y: filterData[0].yData,
+          mode: "lines+markers",
+          name: "合同功率曲线",
+          line: {
+            color: "red",
+            width: 1,
+          },
+          marker: { color: "red", size: 4 },
+        });
+      }
+
+      // 准备布局配置
+      const layout = {
+        title: {
+          text: `有功功率曲线分析${enginName}`,
+          font: {
+            size: 16,
+            weight: "bold",
+          },
+        },
+        xaxis: {
+          title: {
+            text: "风速(m/s)" || "x轴",
+          },
+          gridcolor: "rgb(255,255,255)",
+          tickcolor: "rgb(255,255,255)",
+          backgroundcolor: "#e5ecf6",
+        },
+        yaxis: {
+          title: { text: "功率(kW)" || "Y轴" },
+          gridcolor: "rgb(255,255,255)",
+          tickcolor: "rgb(255,255,255)",
+          backgroundcolor: "#e5ecf6",
+        },
+        margin: {
+          l: 50,
+          r: 50,
+          t: 50,
+          b: 50,
+        },
+        autosize: true,
+        plot_bgcolor: "#e5ecf6",
+        gridcolor: "#fff",
+        bgcolor: "#e5ecf6",
+        legend: {
+          orientation: "h",
+          xanchor: "center",
+          x: 0.5,
+          y: -0.2,
+        },
+      };
+
+      // 创建HTML内容
+      const htmlContent = `
+        <!DOCTYPE html>
+        <html>
+          <head>
+            <script>${plotlyContent}</script>
+            <style>
+              body { margin: 0; }
+              #chart { width: 800px; height: 600px; }
+            </style>
+          </head>
+          <body>
+            <div id="chart"></div>
+            <script>
+              window.onload = function() {
+                const data = ${JSON.stringify(finalData)};
+                const layout = ${JSON.stringify(layout)};
+                Plotly.newPlot('chart', data, layout, {
+                  responsive: true
+                }).then(() => {
+                  window.chartRendered = true;
+                });
+              };
+            </script>
+          </body>
+        </html>
+      `;
+
+      // 设置页面内容
+      await page.setContent(htmlContent, {
+        waitUntil: "networkidle0",
+      });
+
+      // 等待图表渲染完成
+      await page.waitForFunction(() => window.chartRendered === true, {
+        timeout: 60000,
+      });
+
+      // 截图并保存到临时文件
+      // 截图并保存到临时文件
+      const chartElement = await page.$("#chart");
+      await chartElement.screenshot({
+        path: tempFilePath,
+        type: "jpeg",
+      });
+
+      // 上传图片到服务器
+      const formData = new FormData();
+      formData.append("file", fs.createReadStream(tempFilePath));
+      // return formData;
+      // 发送上传请求
+      const response = await axios.post(
+        `${process.env.API_BASE_URL}/examples/upload`,
+        { filePath: tempFilePath, bucketName, objectName }
+      );
+      return response?.data?.url;
+    } finally {
+      await browser.close();
+    }
+  } catch (error) {
+    console.error("生成折线图失败:", error);
+    throw error;
+  }
+};

+ 152 - 166
downLoadServer/src/server/utils/chartsCom/Radar.js

@@ -3,182 +3,168 @@ import fs from "fs-extra";
 import path from "path";
 import FormData from "form-data";
 import { colorSchemes } from "../colors.js";
-import axios from "axios"; // 导入 axios
+import axios from "axios";
+// 获取 plotly.js 的绝对路径
+const plotlyPath = path.join(
+  process.cwd(),
+  "src",
+  "public",
+  "js",
+  "echarts.min.js"
+);
+const plotlyContent = await fs.readFile(plotlyPath, "utf-8");
 
-export const getRadarCharts = async (data, bucketName, objectName) => {
-  try {
-    const colorsBar = colorSchemes[0].colors;
-    // 创建临时目录
-    const tempDir = path.join(process.cwd(), "images");
-    await fs.ensureDir(tempDir);
-    const tempFilePath = path.join(
-      tempDir,
-      `temp_scatter_chart_${Date.now()}.png`
-    );
+// HTML 模板
+const getHtmlContent = () => `
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8" />
+  <title>Radar Chart</title>
+  <script>${plotlyContent}</script>
+  <style>
+    html, body { margin: 0; padding: 0; width: 600px; height: 600px; }
+    #chart { width: 100%; height: 100%; }
+  </style>
+</head>
+<body>
+  <div id="chart"></div>
+  <script>
+    window.renderChart = function (chartData, itemCsvData) {
+      function calcValues(data) {
+        const matrix = data.map((item) => [
+          item.TurbinePowerRate,
+          item.TurbineRunRate,
+          item.WindSpeedAvr,
+          item.Thi,
+          item.Ws,
+        ]);
+        const max = matrix[0].map((_, i) => Math.max(...matrix.map(row => row[i])));
+        const min = matrix[0].map((_, i) => Math.min(...matrix.map(row => row[i])));
+        const median = matrix[0].map((_, i) => {
+          const sorted = matrix.map(row => row[i]).sort((a, b) => a - b);
+          const mid = Math.floor(sorted.length / 2);
+          return sorted.length % 2 === 0
+            ? (sorted[mid - 1] + sorted[mid]) / 2
+            : sorted[mid];
+        });
+        return { max, min, median };
+      }
 
-    // 获取 plotly.js 的绝对路径
-    const plotlyPath = path.join(
-      process.cwd(),
-      "src",
-      "public",
-      "js",
-      "plotly-3.0.1.min.js"
-    );
-    const plotlyContent = await fs.readFile(plotlyPath, "utf-8");
+      const { max, min, median } = calcValues(itemCsvData);
+      const values = [
+        chartData.TurbinePowerRate,
+        chartData.TurbineRunRate,
+        chartData.WindSpeedAvr,
+        chartData.Thi,
+        chartData.Ws,
+      ];
 
-    // 创建浏览器实例
-    const browser = await puppeteer.launch({
-      headless: "new",
-      args: ["--no-sandbox", "--disable-setuid-sandbox"],
-    });
+      const indicators = [
+        { name: "风机能量利用率", max: max[0], min: min[0] },
+        { name: "风机可利用率", max: max[1], min: min[1] },
+        { name: "平均风速", max: max[2], min: min[2] },
+        { name: "等效利用小时", max: max[3], min: min[3] },
+        { name: "功率曲线一致性系数", max: max[4], min: min[4] },
+      ];
 
-    try {
-      // 计算最大值、最小值和中位值
-      const values = calculateValues(data);
-      drawRadarChart(values);
-    } catch (error) {
-      console.error("绘制雷达图失败:", error);
-    } finally {
-      await browser.close();
-    }
-  } catch (error) {
-    console.error("发生错误:", error);
-  }
-};
-
-// 计算最大值、最小值和中位值
-function calculateValues(data) {
-  const itemCsvData = data; // 假设 data 是传入的 CSV 数据
-  const processedData = itemCsvData.map((item) => [
-    item.TurbinePowerRate,
-    item.TurbineRunRate,
-    item.WindSpeedAvr,
-    item.Thi,
-    item.Ws,
-  ]);
-
-  // 计算最大值
-  const maxValues = processedData[0].map((_, colIndex) =>
-    Math.max(...processedData.map((row) => row[colIndex]))
-  );
+      const chart = echarts.init(document.getElementById("chart"));
+      chart.setOption({
+        title: {
+          text: chartData.wind_turbine_name + "机组指标",
+          left: "center",
+        },
+        radar: {
+          indicator: indicators,
+          center: ["50%", "50%"],
+          radius: "60%",
+        },
+        series: [
+          {
+            type: "radar",
+            data: [
+              {
+                value: values,
+                name: chartData.wind_turbine_name,
+                areaStyle: { color: "rgba(99,110,252,0.3)" },
+              },
+              {
+                value: median,
+                name: "中位值",
+                lineStyle: {
+                  type: "dashed",
+                  color: "#f39c12",
+                },
+              },
+            ],
+          },
+        ],
+      });
+    };
+  </script>
+</body>
+</html>
+`;
 
-  // 计算最小值
-  const minValues = processedData[0].map((_, colIndex) =>
-    Math.min(...processedData.map((row) => row[colIndex]))
+export const getRadarCharts = async (
+  chartData,
+  itemCsvData,
+  bucketName,
+  objectName
+) => {
+  const browser = await puppeteer.launch();
+  const page = await browser.newPage();
+  // 创建临时目录
+  const tempDir = path.join(process.cwd(), "images");
+  await fs.ensureDir(tempDir);
+  const tempFilePath = path.join(
+    tempDir,
+    `temp_scatter_chart_${Date.now()}.jpeg`
   );
 
-  // 计算中位值
-  const medianValues = calculateMedian(processedData);
+  await page.setContent(getHtmlContent(), { waitUntil: "load" });
 
-  return { maxValues, minValues, medianValues };
-}
+  // 等待 renderChart 被定义
+  await page.waitForFunction(() => typeof window.renderChart === "function");
 
-// 计算每列的中位值
-function calculateMedian(data) {
-  return data[0].map((_, colIndex) => {
-    const columnData = data
-      .map((row) => parseFloat(row[colIndex])) // 将字符串转换为数字
-      .sort((a, b) => a - b); // 数字排序
-    const mid = Math.floor(columnData.length / 2);
-    return columnData.length % 2 === 0
-      ? (columnData[mid - 1] + columnData[mid]) / 2
-      : columnData[mid];
-  });
-}
+  // 调用渲染函数
+  await page.evaluate(
+    (chartData, itemCsvData) => {
+      window.renderChart(chartData, itemCsvData);
+    },
+    chartData,
+    itemCsvData
+  );
 
-// 渲染雷达图
-function drawRadarChart({ maxValues, minValues, medianValues }) {
-  // 获取数据
-  const values = [
-    // 这里需要根据实际数据填充
-  ];
+  // 再等待图表渲染完成(给 ECharts 时间)
+  (await page.waitForTimeout)
+    ? page.waitForTimeout(1000)
+    : new Promise((res) => setTimeout(res, 1000));
 
-  // 雷达图的指示器,使用最大值、最小值和中位值来动态设置
-  const indicators = [
-    {
-      name: "风机能量利用率",
-      max: maxValues[0],
-      min: minValues[0],
-    },
-    {
-      name: "风机可利用率",
-      max: maxValues[1],
-      min: minValues[1],
-    },
-    { name: "平均风速", max: maxValues[2], min: minValues[2] },
-    {
-      name: "等效利用小时",
-      max: maxValues[3],
-      min: minValues[3],
-    },
-    {
-      name: "功率曲线一致性系数",
-      max: maxValues[4],
-      min: minValues[4],
-    },
-  ];
+  // 上传逻辑
+  // 截图并保存到临时文件
+  const chartElement = await page.$("#chart");
+  await chartElement.screenshot({
+    path: tempFilePath,
+    type: "jpeg",
+  });
 
-  // 构建 ECharts 配置项
-  const option = {
-    title: {
-      text: "机组指标",
-      left: "center",
-      textStyle: {
-        fontSize: 16,
-      },
-    },
-    tooltip: {
-      trigger: "item",
-      formatter: (params) => {
-        const indicators = [
-          { label: "风机能量利用率", unit: " %" },
-          { label: "风机可利用率", unit: " %" },
-          { label: "平均风速", unit: " m/s" },
-          { label: "利用小时", unit: " h" },
-          { label: "功率曲线一致性系数", unit: " %" },
-        ];
-        const values = params.value;
-        let content = `<strong>${params.seriesName}</strong><br/>`;
-        for (let i = 0; i < indicators.length; i++) {
-          content += `${indicators[i].label}: ${values[i]}${
-            indicators[i].unit
-          }<br/>中位值: ${parseFloat(medianValues[i].toFixed(3))}${
-            indicators[i].unit
-          }<br/><br/>`;
-        }
-        return content;
-      },
-    },
-    radar: {
-      indicator: indicators,
-      center: ["45%", "45%"],
-      radius: "50%",
-    },
-    series: [
-      {
-        name: "机组指标",
-        type: "radar",
-        data: [
-          {
-            value: values,
-            name: "机组指标",
-          },
-        ],
-      },
+  try {
+    const newUrl = objectName.substring(0, objectName.lastIndexOf("/"));
+    // 上传图片到服务器
+    const formData = new FormData();
+    formData.append("file", fs.createReadStream(tempFilePath));
+    // 发送上传请求
+    const response = await axios.post(
+      `${process.env.API_BASE_URL}/examples/upload`,
       {
-        name: "中位值",
-        type: "radar",
-        data: [
-          {
-            value: medianValues,
-            name: "中位值",
-          },
-        ],
-      },
-    ],
-  };
-
-  // 使用 ECharts 渲染雷达图
-  const chart = echarts.init(this.$refs.radarChart);
-  chart.setOption(option);
-}
+        filePath: tempFilePath,
+        bucketName,
+        objectName: newUrl + "/" + chartData.wind_turbine_name + ".jpg",
+      }
+    );
+    return response?.data?.url;
+  } catch (error) {
+    console.error("❌ 上传失败:", error.message);
+  }
+};

+ 17 - 16
downLoadServer/src/server/utils/chartsCom/Time3DChart.js

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2025-04-14 17:49:33
- * @LastEditTime: 2025-05-08 15:26:03
+ * @LastEditTime: 2025-05-21 15:11:06
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/downLoadServer/src/server/utils/chartsCom/Time3DChart.js
@@ -26,7 +26,7 @@ export const generateTime3DChart = async (data, bucketName, objectName) => {
     await fs.ensureDir(tempDir);
     const tempFilePath = path.join(
       tempDir,
-      `temp_heatmap_chart_${Date.now()}.png`
+      `temp_heatmap_chart_${Date.now()}.jpeg`
     );
 
     // 获取 plotly.js 的绝对路径
@@ -133,20 +133,20 @@ export const generateTime3DChart = async (data, bucketName, objectName) => {
               text: data.zaixs,
             },
           },
-          aspectratio: {
-            x: 2.2,
-            y: 1.7,
-            z: 1,
-          },
           plot_bgcolor: "#e5ecf6",
           gridcolor: "#fff",
           bgcolor: "#e5ecf6", // 设置背景颜色
           aspectmode: "manual",
+          aspectratio: {
+            x: 2.0000000000000018,
+            y: 1.5454545454545465,
+            z: 0.9090909090909098,
+          },
           camera: {
             up: {
-              x: 0.200292643688136,
-              y: 0.2488259353493132,
-              z: 0.947612004346693,
+              x: 0.19380723218588866,
+              y: 0.2540224158731985,
+              z: 0.9475818534492884,
             },
             center: {
               x: -0.052807476121180814,
@@ -154,9 +154,9 @@ export const generateTime3DChart = async (data, bucketName, objectName) => {
               z: -0.022911006648570736,
             },
             eye: {
-              x: -2.126379643342493,
-              y: -2.551422475965373,
-              z: 1.0917667684145647,
+              x: -2.126389777109588,
+              y: -2.5514260394238466,
+              z: 1.091739681861482,
             },
             projection: {
               type: "orthographic",
@@ -179,11 +179,11 @@ export const generateTime3DChart = async (data, bucketName, objectName) => {
         <html>
         <head>
           <meta charset="UTF-8">
-          <title>热力图</title>
+          <title>3D图</title>
           <script>${plotlyContent}</script>
         </head>
         <body>
-          <div id="chart" style="width: 100%; height: 600px"></div>
+          <div id="chart" style="width: 100%; height: 500px"></div>
           <script>
             const traces = ${JSON.stringify(traces)};
             const layout = ${JSON.stringify(layout)};
@@ -212,7 +212,7 @@ export const generateTime3DChart = async (data, bucketName, objectName) => {
       const chartElement = await page.$("#chart");
       await chartElement.screenshot({
         path: tempFilePath,
-        type: "png",
+        type: "jpeg",
       });
 
       // 上传图片到服务器
@@ -224,6 +224,7 @@ export const generateTime3DChart = async (data, bucketName, objectName) => {
         `${process.env.API_BASE_URL}/examples/upload`,
         { filePath: tempFilePath, bucketName, objectName }
       );
+      return response?.data?.url;
     } catch (error) {
       console.error("生成3D图失败:", error);
       throw error;

+ 3 - 2
downLoadServer/src/server/utils/chartsCom/TwoDMarkersChart.js

@@ -25,7 +25,7 @@ export const generateTwoDMarkersChart = async (
     await fs.ensureDir(tempDir);
     const tempFilePath = path.join(
       tempDir,
-      `temp_scatter_chart_${Date.now()}.png`
+      `temp_scatter_chart_${Date.now()}.jpeg`
     );
 
     // 获取 plotly.js 的绝对路径
@@ -163,7 +163,7 @@ export const generateTwoDMarkersChart = async (
       const chartElement = await page.$("#chart");
       await chartElement.screenshot({
         path: tempFilePath,
-        type: "png",
+        type: "jpeg",
       });
 
       // 上传图片到服务器
@@ -175,6 +175,7 @@ export const generateTwoDMarkersChart = async (
         `${process.env.API_BASE_URL}/examples/upload`,
         { filePath: tempFilePath, bucketName, objectName }
       );
+      return response?.data?.url;
     } catch (error) {
       console.error("生成2D散点图失败:", error);
       throw error;

+ 3 - 2
downLoadServer/src/server/utils/chartsCom/TwoDMarkersChart1.js

@@ -16,7 +16,7 @@ export const generateTwoDMarkersChart1 = async (
     await fs.ensureDir(tempDir);
     const tempFilePath = path.join(
       tempDir,
-      `temp_scatter_chart_${Date.now()}.png`
+      `temp_scatter_chart_${Date.now()}.jpeg`
     );
 
     // 获取 plotly.js 的绝对路径
@@ -185,7 +185,7 @@ export const generateTwoDMarkersChart1 = async (
       const chartElement = await page.$("#chart");
       await chartElement.screenshot({
         path: tempFilePath,
-        type: "png",
+        type: "jpeg",
       });
 
       // 上传图片到服务器
@@ -197,6 +197,7 @@ export const generateTwoDMarkersChart1 = async (
         `${process.env.API_BASE_URL}/examples/upload`,
         { filePath: tempFilePath, bucketName, objectName }
       );
+      return response?.data?.url;
     } catch (error) {
       console.error("生成2D散点图失败:", error);
       throw error;

+ 185 - 0
downLoadServer/src/server/utils/chartsCom/WindRoseChart.js

@@ -0,0 +1,185 @@
+import puppeteer from "puppeteer";
+import fs from "fs-extra";
+import path from "path";
+import FormData from "form-data";
+import axios from "axios"; // 导入 axios
+import { colorSchemes } from "../colors.js";
+
+export const getWindRoseChart = async (
+  data,
+  bucketName,
+  objectName,
+  analysisTypeCode
+) => {
+  try {
+    // 获取 plotly.js 的绝对路径
+    const plotlyPath = path.join(
+      process.cwd(),
+      "src",
+      "public",
+      "js",
+      "plotly-3.0.1.min.js"
+    );
+    const plotlyContent = await fs.readFile(plotlyPath, "utf-8");
+
+    const windRoseList = data.data; // 确保数据源一致
+    // 创建临时目录
+    const tempDir = path.join(process.cwd(), "images");
+    await fs.ensureDir(tempDir);
+    const tempFilePath = path.join(
+      tempDir,
+      `temp_wind_rose_chart_${Date.now()}.jpeg`
+    );
+
+    // 定义风向的 16 等分
+    const windDirections = Array.from({ length: 16 }, (_, i) => i * 22.5);
+    const windSpeedRanges = new Set();
+    const counts = {};
+
+    // 从数据中提取 windSpeedRange 和动态生成 speedLabels
+    windRoseList.forEach((engine) => {
+      engine.windRoseData.forEach((item) => {
+        windSpeedRanges.add(item.windSpeedRange);
+      });
+    });
+
+    const speedLabels = Array.from(windSpeedRanges).sort(); // 动态范围值
+    const colors = colorSchemes[0].colors; // 生成颜色
+    const colorscale = {};
+    speedLabels.forEach((label, index) => {
+      colorscale[label] = colors[(index % colors.length) + 4];
+    });
+
+    // 初始化 counts 对象
+    speedLabels.forEach((speedLabel) => {
+      counts[speedLabel] = Array(windDirections.length).fill(0); // 确保初始化为 0
+    });
+
+    // 数据聚合
+    windRoseList.forEach((engine) => {
+      engine.windRoseData.forEach((item) => {
+        const { windDirection, windSpeedRange } = item;
+        const index = windDirections.findIndex(
+          (dir, i) =>
+            windDirection >= dir && windDirection < windDirections[i + 1]
+        );
+
+        if (index !== -1 && counts[windSpeedRange]) {
+          counts[windSpeedRange][index] += item.frequency; // 聚合频率
+        }
+      });
+    });
+
+    // 构建 traces
+    const traces = speedLabels.map((speedLabel) => {
+      const percentage = counts[speedLabel];
+
+      return {
+        r: percentage,
+        theta: windDirections,
+        name: speedLabel,
+        type: "barpolar",
+        marker: {
+          color: colorscale[speedLabel],
+          line: {
+            color: "white",
+            width: 1,
+          },
+        },
+        hovertemplate: `频率: %{r} <br> 风向: %{theta} <br> <extra></extra>`, // 修改为与 Vue 文件一致
+      };
+    });
+
+    // 图表布局
+    const layout = {
+      title: {
+        text: `风向玫瑰分析 - ${windRoseList[0]?.enginName} `, // 修改为与 Vue 文件一致
+        font: {
+          size: 16,
+          weight: "bold",
+        },
+      },
+      plot_bgcolor: "#e5ecf6",
+      polar: {
+        bgcolor: "#e5ecf6",
+        radialaxis: {
+          title: { text: data?.axes?.radial || "频率百分比(%)" }, // 确保与 Vue 文件一致
+          gridcolor: "rgb(255,255,255)",
+          showgrid: true,
+          linecolor: "rgb(255,255,255)",
+        },
+        angularaxis: {
+          title: { text: "风向" }, // 确保与 Vue 文件一致
+          tickvals: windDirections,
+          ticktext: windDirections.map((item) => item), // 设置为空字符串以隐藏单位
+          gridcolor: "rgb(255,255,255)",
+          tickcolor: "rgb(255,255,255)",
+          linecolor: "rgb(255,255,255)",
+        },
+      },
+      showlegend: true,
+      legend: { title: { text: "风速范围" } }, // 确保与 Vue 文件一致
+    };
+
+    // 使用 Puppeteer 生成图表的截图
+    const browser = await puppeteer.launch({
+      headless: "new",
+      args: ["--no-sandbox", "--disable-setuid-sandbox"],
+    });
+    try {
+      const page = await browser.newPage();
+      const htmlContent = `
+        <!DOCTYPE html>
+        <html>
+          <head>
+            <meta charset="UTF-8">
+            <title>玫瑰图</title>
+            <script>${plotlyContent}</script>
+            <style>
+              body { margin: 0; }
+              #chart { width: 800px; height: 600px; }
+            </style>
+          </head>
+          <body>
+            <div id="chart"></div>
+            <script>
+              window.onload = function() {
+                Plotly.newPlot('chart', ${JSON.stringify(
+                  traces
+                )}, ${JSON.stringify(layout)}).then(() => {
+                  window.chartRendered = true;
+                });
+              };
+            </script>
+          </body>
+        </html>
+      `;
+      await page.setContent(htmlContent, { waitUntil: "networkidle0" });
+      await page.waitForFunction(() => window.chartRendered === true, {
+        timeout: 60000,
+      });
+      // 截图并保存到临时文件
+      const chartElement = await page.$("#chart");
+      await chartElement.screenshot({ path: tempFilePath, type: "jpeg" });
+      // 上传图片到服务器
+      const formData = new FormData();
+      formData.append("file", fs.createReadStream(tempFilePath));
+      const response = await axios.post(
+        `${process.env.API_BASE_URL}/examples/upload`,
+        {
+          filePath: tempFilePath,
+          bucketName,
+          objectName,
+        }
+      );
+      console.log("上传成功:", response.data);
+      return response?.data?.url;
+    } catch (error) {
+      console.error("生成图表失败:", error);
+    } finally {
+      await browser.close();
+    }
+  } catch (error) {
+    console.error("发生错误:", error);
+  }
+};

+ 181 - 0
downLoadServer/src/server/utils/chartsCom/YewErrorBarChart.js

@@ -0,0 +1,181 @@
+import puppeteer from "puppeteer";
+import fs from "fs-extra";
+import path from "path";
+import FormData from "form-data";
+import axios from "axios"; // 导入 axios
+import { colorSchemes } from "../colors.js";
+
+export const getYewErrorBarChart = async (
+  data,
+  bucketName,
+  objectName,
+  analysisTypeCode
+) => {
+  try {
+    // 提取机组编号和偏航误差值
+    const xData = data.map((item) => item.engine_name); // 机组编号
+    const yData = data.map((item) => item.yaw_error1); // 偏航误差值
+
+    // 为每个数据点分配颜色
+    const colors = yData.map((value) => {
+      if (value <= 3) {
+        return "#8AC8BE"; // (0, 3] 蓝色
+      } else if (value <= 5) {
+        return "#407DB3"; // (3, 5] 绿色
+      } else {
+        return "#1B2973"; // (5, ∞] 红色
+      }
+    });
+
+    const trace = {
+      x: xData, // 横坐标数据
+      y: yData, // 纵坐标数据
+      type: "bar", // 当前图表类型
+      marker: {
+        color: colors, // 每个点的颜色
+      },
+      name: "偏航误差值", // 图例名称
+    };
+    // 创建虚拟的 trace 以便显示图例
+    const legendTrace1 = {
+      x: [null],
+      y: [null],
+      name: "(0, 3]",
+      mode: "markers",
+      marker: { color: "#8AC8BE", size: 10 },
+    };
+    const legendTrace2 = {
+      x: [null],
+      y: [null],
+      name: "(3, 5]",
+      mode: "markers",
+      marker: { color: "#407DB3", size: 10 },
+    };
+    const legendTrace3 = {
+      x: [null],
+      y: [null],
+      name: "(5, ∞]",
+      mode: "markers",
+      marker: { color: "#1B2973", size: 10 },
+    };
+    const layout = {
+      title: {
+        text: "机组静态偏航误差值", // 图表标题
+        font: {
+          size: 16, // 设置标题字体大小(默认 16)
+          weight: "bold",
+        },
+      },
+      xaxis: {
+        title: {
+          text: "机组编号",
+        }, // 横坐标标题
+        tickmode: "array",
+        tickvals: xData, // 设置刻度值(机组编号)
+        ticktext: xData, // 设置刻度文本(机组编号)
+        gridcolor: "rgb(255,255,255)",
+        tickcolor: "rgb(255,255,255)",
+        backgroundcolor: "#e5ecf6",
+      },
+      yaxis: {
+        title: {
+          text: "静态偏航误差值(度)",
+        }, // 纵坐标标题
+        gridcolor: "rgb(255,255,255)",
+        tickcolor: "rgb(255,255,255)",
+        backgroundcolor: "#e5ecf6",
+      },
+      margin: {
+        l: 50,
+        r: 50,
+        t: 50,
+        b: 50,
+      },
+      plot_bgcolor: "#e5ecf6",
+      gridcolor: "#fff",
+      bgcolor: "#e5ecf6", // 设置背景颜色
+      autosize: true, // 开启自适应
+      showlegend: true, // 显示图例
+    };
+
+    // 创建临时目录
+    const tempDir = path.join(process.cwd(), "images");
+    await fs.ensureDir(tempDir);
+    const tempFilePath = path.join(
+      tempDir,
+      `temp_yew_error_bar_chart_${Date.now()}.jpeg`
+    );
+
+    // 获取 plotly.js 的绝对路径
+    const plotlyPath = path.join(
+      process.cwd(),
+      "src",
+      "public",
+      "js",
+      "plotly-3.0.1.min.js"
+    );
+    const plotlyContent = await fs.readFile(plotlyPath, "utf-8");
+
+    // 使用 Puppeteer 生成图表的截图
+    const browser = await puppeteer.launch({
+      headless: "new",
+      args: ["--no-sandbox", "--disable-setuid-sandbox"],
+    });
+    try {
+      const page = await browser.newPage();
+      const traces = [trace, legendTrace1, legendTrace2, legendTrace3];
+      const htmlContent = `
+        <!DOCTYPE html>
+        <html>
+          <head>
+            <meta charset="UTF-8">
+            <title>机组静态偏航误差值</title>
+            <script>${plotlyContent}</script>
+            <style>
+              body { margin: 0; }
+              #chart { width: 800px; height: 600px; }
+            </style>
+          </head>
+          <body>
+            <div id="chart"></div>
+            <script>
+              window.onload = function() {
+                Plotly.newPlot('chart', ${JSON.stringify(
+                  traces
+                )}, ${JSON.stringify(layout)}).then(() => {
+                  window.chartRendered = true;
+                });
+              };
+            </script>
+          </body>
+        </html>
+      `;
+      await page.setContent(htmlContent, { waitUntil: "networkidle0" });
+      await page.waitForFunction(() => window.chartRendered === true, {
+        timeout: 60000,
+      });
+      // 截图并保存到临时文件
+      const chartElement = await page.$("#chart");
+      await chartElement.screenshot({ path: tempFilePath, type: "jpeg" });
+      // 上传图片到服务器
+      const formData = new FormData();
+      formData.append("file", fs.createReadStream(tempFilePath));
+      const response = await axios.post(
+        `${process.env.API_BASE_URL}/examples/upload`,
+        {
+          filePath: tempFilePath,
+          bucketName,
+          objectName,
+        }
+      );
+      console.log("上传成功:", response.data);
+      return response?.data?.url;
+    } catch (error) {
+      console.error("生成图表失败:", error);
+    } finally {
+      await browser.close();
+    }
+  } catch (error) {
+    console.error("发生错误:", error);
+  }
+};

+ 3 - 17
downLoadServer/src/server/utils/chartsCom/lineAndChildLine.js

@@ -104,7 +104,7 @@ export const generateLineAndChildLine = async (
     await fs.ensureDir(tempDir);
     const tempFilePath = path.join(
       tempDir,
-      `temp_line_chart_${Date.now()}.png`
+      `temp_line_chart_${Date.now()}.jpeg`
     );
 
     // 获取 plotly.js 的绝对路径
@@ -168,7 +168,7 @@ export const generateLineAndChildLine = async (
       const chartElement = await page.$("#chart");
       await chartElement.screenshot({
         path: tempFilePath,
-        type: "png",
+        type: "jpeg",
       });
 
       // 上传图片到服务器
@@ -179,26 +179,12 @@ export const generateLineAndChildLine = async (
       formData.append("engineCode", chartData.engineCode || "");
       formData.append("analysisTypeCode", chartData.analysisTypeCode || "");
 
-      // const uploadResponse = await axios.post(
-      //   "http://localhost:6900/upload",
-      //   formData,
-      //   {
-      //     headers: {
-      //       ...formData.getHeaders(),
-      //     },
-      //   }
-      // );
-
-      // 删除临时文件
-      // await fs.remove(tempFilePath);
-      // console.log("折线图生成并上传成功:", uploadResponse.data.url);
-      // return uploadResponse.data.url;
-      // return formData;
       // 发送上传请求
       const response = await axios.post(
         `${process.env.API_BASE_URL}/examples/upload`,
         { filePath: tempFilePath, bucketName, objectName }
       );
+      return response?.data?.url;
     } finally {
       await browser.close();
     }

+ 8 - 19
downLoadServer/src/server/utils/chartsCom/lineChartsFen.js

@@ -17,15 +17,12 @@ export const generateLineChart = async (
   fieldEngineCode
 ) => {
   try {
-    console.log("开始生成折线图...");
-    console.log("数据:", data);
-
     // 创建临时目录
     const tempDir = path.join(process.cwd(), "images");
     await fs.ensureDir(tempDir);
     const tempFilePath = path.join(
       tempDir,
-      `temp_line_chart_${Date.now()}.png`
+      `temp_line_chart_${Date.now()}.jpeg`
     );
 
     // 获取 plotly.js 的绝对路径
@@ -65,6 +62,7 @@ export const generateLineChart = async (
       });
 
       const finalData = [];
+      console.log(fieldEngineCode, "fieldEngineCode");
       sortedData.forEach((turbine) => {
         const color =
           turbine.engineCode === fieldEngineCode ? "#406DAB" : "#D3D3D3";
@@ -174,7 +172,7 @@ export const generateLineChart = async (
       const chartElement = await page.$("#chart");
       await chartElement.screenshot({
         path: tempFilePath,
-        type: "png",
+        type: "jpeg",
       });
 
       // 上传图片到服务器
@@ -184,26 +182,17 @@ export const generateLineChart = async (
       formData.append("engineCode", data.engineCode);
       formData.append("analysisTypeCode", data.analysisTypeCode);
 
-      // const uploadResponse = await axios.post(
-      //   "http://localhost:6900/upload",
-      //   formData,
-      //   {
-      //     headers: {
-      //       ...formData.getHeaders(),
-      //     },
-      //   }
-      // );
-
       // 删除临时文件
       // await fs.remove(tempFilePath);
       // console.log("折线图生成并上传成功:", uploadResponse.data.url);
       // return uploadResponse.data.url;
       // return formData;
       // 发送上传请求
-      // const response = await axios.post(
-      //   `${process.env.API_BASE_URL}/examples/upload`,
-      //   { filePath: tempFilePath, bucketName, objectName }
-      // );
+      const response = await axios.post(
+        `${process.env.API_BASE_URL}/examples/upload`,
+        { filePath: tempFilePath, bucketName, objectName }
+      );
+      return response?.data?.url;
     } finally {
       await browser.close();
     }

+ 4 - 4
downLoadServer/src/server/utils/chartsCom/powerMarkers2DCharts.js

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2025-04-28 14:15:23
- * @LastEditTime: 2025-05-06 10:33:43
+ * @LastEditTime: 2025-05-26 11:01:22
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /downLoadServer/src/server/utils/chartsCom/powerMarkers2DCharts.js
@@ -25,7 +25,7 @@ export const generatepowerMarkers2DCharts = async (
     await fs.ensureDir(tempDir);
     const tempFilePath = path.join(
       tempDir,
-      `temp_scatter_chart_${Date.now()}.png`
+      `temp_scatter_chart_${Date.now()}.jpeg`
     );
 
     // 获取 plotly.js 的绝对路径
@@ -232,7 +232,7 @@ export const generatepowerMarkers2DCharts = async (
       const chartElement = await page.$("#chart");
       await chartElement.screenshot({
         path: tempFilePath,
-        type: "png",
+        type: "jpeg",
       });
 
       // 上传图片到服务器
@@ -250,7 +250,7 @@ export const generatepowerMarkers2DCharts = async (
       );
       console.log(response.data, "response 上传结果"); // 打印上传结果
       // return response.data; // 返回上传结果
-      return "成功";
+      return response?.data?.url;
     } catch (error) {
       console.error("生成2D散点图失败:", error);
       throw error;

+ 166 - 0
downLoadServer/src/server/utils/chartsCom/yawErrorBarSum.js

@@ -0,0 +1,166 @@
+import puppeteer from "puppeteer";
+import fs from "fs-extra";
+import path from "path";
+import FormData from "form-data";
+import axios from "axios"; // 导入 axios
+import { colorSchemes } from "../colors.js";
+import { text } from "stream/consumers";
+
+export const getYawErrorBarSumCharts = async (
+  data,
+  bucketName,
+  objectName,
+  analysisTypeCode
+) => {
+  try {
+    // 提取静态偏航误差值和机组数量
+    let a = [];
+    let b = [];
+    let c = [];
+    data.map((item) => {
+      if (item["[0,3]"] != "0.0") {
+        a.push(item["[0,3]"]);
+      }
+      if (item["(3,5]"] != "0.0") {
+        b.push(item["(3,5]"]);
+      }
+      if (item["(5, )"] != "0.0") {
+        c.push(item["(5, )"]);
+      }
+    });
+
+    const xData = ["(0,3]", "(3,5]", "(>5]"]; // 偏航误差区间
+    const yData = [a.length, b.length, c.length]; // 各区间的机组数量
+
+    // 每个柱子的颜色
+    const colors = [
+      "#8AC8BE", // (0, 3] 蓝色
+      "#407DB3", // (3, 5] 绿色
+      "#1B2973", // (5, ∞] 红色
+    ];
+
+    const trace = {
+      x: xData, // 横坐标数据
+      y: yData, // 纵坐标数据
+      type: "bar", // 当前图表类型
+      marker: {
+        color: colors, // 为每个柱子分配不同的颜色
+      },
+      // hovertemplate:
+      //   `偏航误差值:` + ` %{x} <br> ` + `台数:` + "%{y} <br> <extra></extra>",
+    };
+
+    const layout = {
+      title: {
+        text: "静态偏航误差的绝对值机组台数分布情况",
+        font: {
+          size: 16,
+          weight: "bold",
+        },
+      }, // 图表标题
+      xaxis: {
+        title: { text: "静态偏航误差值(度)" }, // 横坐标标题
+        gridcolor: "rgb(255,255,255)",
+        tickcolor: "rgb(255,255,255)",
+        backgroundcolor: "#e5ecf6",
+      },
+      yaxis: {
+        title: {
+          text: "台数",
+        }, // 纵坐标标题
+        gridcolor: "rgb(255,255,255)",
+        tickcolor: "rgb(255,255,255)",
+        backgroundcolor: "#e5ecf6",
+      },
+      margin: {
+        l: 50,
+        r: 50,
+        t: 50,
+        b: 50,
+      },
+      autosize: true, // 开启自适应
+      // showlegend: true, // 显示图例
+      plot_bgcolor: "#e5ecf6",
+      gridcolor: "#fff",
+      bgcolor: "#e5ecf6", // 设置背景颜色
+    };
+
+    // 创建临时目录
+    const tempDir = path.join(process.cwd(), "images");
+    await fs.ensureDir(tempDir);
+    const tempFilePath = path.join(
+      tempDir,
+      `temp_yaw_error_bar_sum_chart_${Date.now()}.jpeg`
+    );
+    // 获取 plotly.js 的绝对路径
+    const plotlyPath = path.join(
+      process.cwd(),
+      "src",
+      "public",
+      "js",
+      "plotly-3.0.1.min.js"
+    );
+    const plotlyContent = await fs.readFile(plotlyPath, "utf-8");
+
+    // 使用 Puppeteer 生成图表的截图
+    const browser = await puppeteer.launch({
+      headless: "new",
+      args: ["--no-sandbox", "--disable-setuid-sandbox"],
+    });
+    try {
+      const page = await browser.newPage();
+      const htmlContent = `
+        <!DOCTYPE html>
+        <html>
+          <head>
+            <meta charset="UTF-8">
+            <title>静态偏航误差值</title>
+            <script>${plotlyContent}</script>
+            <style>
+              body { margin: 0; }
+              #chart { width: 800px; height: 600px; }
+            </style>
+          </head>
+          <body>
+            <div id="chart"></div>
+            <script>
+              window.onload = function() {
+                Plotly.newPlot('chart', [${JSON.stringify(
+                  trace
+                )}], ${JSON.stringify(layout)}).then(() => {
+                  window.chartRendered = true;
+                });
+              };
+            </script>
+          </body>
+        </html>
+      `;
+      await page.setContent(htmlContent, { waitUntil: "networkidle0" });
+      await page.waitForFunction(() => window.chartRendered === true, {
+        timeout: 60000,
+      });
+      // 截图并保存到临时文件
+      const chartElement = await page.$("#chart");
+      await chartElement.screenshot({ path: tempFilePath, type: "jpeg" });
+      // 上传图片到服务器
+      const formData = new FormData();
+      formData.append("file", fs.createReadStream(tempFilePath));
+      const response = await axios.post(
+        `${process.env.API_BASE_URL}/examples/upload`,
+        {
+          filePath: tempFilePath,
+          bucketName,
+          objectName,
+        }
+      );
+      return response.data.url;
+      console.log("上传成功:", response.data);
+    } catch (error) {
+      console.error("生成图表失败:", error);
+    } finally {
+      await browser.close();
+    }
+  } catch (error) {
+    console.error("发生错误:", error);
+  }
+};

+ 190 - 0
downLoadServer/src/server/utils/chartsCom/yawErrorLine.js

@@ -0,0 +1,190 @@
+/*
+ * @Author: your name
+ * @Date: 2025-05-14 10:49:00
+ * @LastEditTime: 2025-05-26 11:02:41
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /downLoadServer/src/server/utils/chartsCom/yawErrorLine.js
+ */
+import puppeteer from "puppeteer";
+import fs from "fs-extra";
+import path from "path";
+import FormData from "form-data";
+import { colorSchemes } from "../colors.js";
+import axios from "axios";
+
+export const generateYawErrorLine = async (
+  chartData,
+  bucketName,
+  objectName
+) => {
+  try {
+    const data = [];
+    const colors = [...colorSchemes[0].colors]; // 生成颜色
+    // 获取 plotly.js 的绝对路径
+    const plotlyPath = path.join(
+      process.cwd(),
+      "src",
+      "public",
+      "js",
+      "plotly-3.0.1.min.js"
+    );
+    const plotlyContent = await fs.readFile(plotlyPath, "utf-8");
+
+    chartData &&
+      chartData.data &&
+      chartData.data.forEach((turbine, index) => {
+        // 判断图表类型,根据类型调整绘制方式
+        const chartConfig = {
+          x: turbine.xData, // X 数据
+          y: turbine.yData, // Y 数据
+          name: turbine.legend, // 使用机组名称
+          line: {
+            color: colors[index % colors.length], // 为每个机组分配不同的颜色
+          },
+          marker: {
+            color: colors[index % colors.length], // 为每个机组分配不同的颜色
+          },
+          hovertemplate:
+            `${chartData.xaixs}:` +
+            ` %{x} <br> ` +
+            `${chartData.yaixs}:` +
+            "%{y} <br> <extra></extra>",
+        };
+
+        if (chartData.chartType === "line") {
+          chartConfig.mode = "lines+markers"; // 如果是折线图
+          chartConfig.fill = "none";
+        } else if (chartData.chartType === "bar") {
+          chartConfig.fill = "tonexty";
+        }
+
+        data.push(chartConfig);
+      });
+
+    const layout = {
+      title: {
+        text: chartData.data[0].title,
+        font: {
+          size: 16, // 设置标题字体大小(默认 16)
+          weight: "bold",
+        },
+      },
+      xaxis: {
+        gridcolor: "rgb(255,255,255)",
+        tickcolor: "rgb(255,255,255)",
+        backgroundcolor: "#e5ecf6",
+        title: chartData.xaixs || "X轴", // 横坐标标题
+      },
+      yaxis: {
+        gridcolor: "rgb(255,255,255)",
+        tickcolor: "rgb(255,255,255)",
+        backgroundcolor: "#e5ecf6",
+        title: chartData.yaixs || "Y轴", // 纵坐标标题
+      },
+      //   margin: {
+      //     l: 50,
+      //     r: 50,
+      //     t: 50,
+      //     b: 50,
+      //   },
+      plot_bgcolor: "#e5ecf6",
+      gridcolor: "#fff",
+      bgcolor: "#e5ecf6", // 设置背景颜色
+      autosize: true, // 开启自适应
+      barmode: chartData.chartType === "bar" ? "stack" : "group", // 如果是柱状图则启用堆叠
+    };
+
+    if (
+      chartData.contract_Cp_curve_yData &&
+      chartData.contract_Cp_curve_yData.length > 0
+    ) {
+      data.push({
+        x: chartData.contract_Cp_curve_xData,
+        y: chartData.contract_Cp_curve_yData,
+        mode: "lines+markers",
+        name: "合同功率曲线",
+        line: {
+          color: "red",
+          width: 1, // 设置线条的宽度为1
+        },
+        marker: { color: "red", size: 4 },
+      });
+    }
+
+    // 使用 Puppeteer 生成图表的截图
+    const browser = await puppeteer.launch({
+      headless: "new",
+      args: ["--no-sandbox", "--disable-setuid-sandbox"],
+    });
+
+    const tempDir = path.join(process.cwd(), "images");
+    await fs.ensureDir(tempDir);
+    const tempFilePath = path.join(
+      tempDir,
+      `temp_yaw_error_chart_${Date.now()}.jpeg`
+    );
+
+    try {
+      const page = await browser.newPage();
+      const htmlContent = `
+        <!DOCTYPE html>
+        <html>
+          <head>
+            <meta charset="UTF-8">
+            <title>静态偏航折线分图</title>
+            <script>${plotlyContent}</script>
+            <style>
+              body { margin: 0; }
+              #chart { width: 800px; height: 600px; }
+            </style>
+          </head>
+          <body>
+            <div id="chart"></div>
+            <script>
+              window.onload = function() {
+                Plotly.newPlot('chart', ${JSON.stringify(
+                  data
+                )}, ${JSON.stringify(layout)}).then(() => {
+                  window.chartRendered = true;
+                });
+              };
+            </script>
+          </body>
+        </html>
+      `;
+
+      await page.setContent(htmlContent, { waitUntil: "networkidle0" });
+      await page.waitForFunction(() => window.chartRendered === true, {
+        timeout: 60000,
+      });
+
+      // 截图并保存到临时文件
+      const chartElement = await page.$("#chart");
+      await chartElement.screenshot({ path: tempFilePath, type: "jpeg" });
+
+      // 上传图片到服务器
+      const formData = new FormData();
+      formData.append("file", fs.createReadStream(tempFilePath));
+      const response = await axios.post(
+        `${process.env.API_BASE_URL}/examples/upload`,
+        {
+          filePath: tempFilePath,
+          bucketName,
+          objectName,
+        }
+      );
+
+      console.log("上传成功:", response.data);
+      return response?.data?.url;
+    } catch (error) {
+      console.error("生成折线图失败:", error);
+      throw error;
+    } finally {
+      await browser.close();
+    }
+  } catch (error) {
+    console.error("生成折线图失败:", error);
+    throw error;
+  }
+};

+ 171 - 0
downLoadServer/src/server/utils/copyFileCsv.js

@@ -0,0 +1,171 @@
+import fs from "fs-extra";
+import path from "path";
+import PizZip from "pizzip";
+import Docxtemplater from "docxtemplater";
+import { fileURLToPath } from "url";
+import ImageModule from "docxtemplater-image-module-free";
+import axios from "axios";
+import sizeOf from "image-size";
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+// 读取本地或远程图片,返回 Buffer
+const getImageFromUrl = async (url) => {
+  if (typeof url !== "string") {
+    throw new TypeError(`图片 URL 应为字符串,但实际是 ${typeof url}`);
+  }
+
+  if (url.startsWith("http")) {
+    const safeUrl = url.replace(/#/g, "%23"); // 处理 URL 中的 #
+    const response = await axios.get(safeUrl, { responseType: "arraybuffer" });
+    const buffer = Buffer.from(response.data, "binary");
+
+    if (buffer.length < 1000) {
+      console.warn(`⚠️ 图像可能无效或过小: ${url}`);
+    }
+
+    return buffer;
+  } else {
+    return fs.readFileSync(url); // 本地图片
+  }
+};
+
+// 预加载所有图片为 Buffer
+const preloadAllImages = async (chartsImages) => {
+  const allUrls = Object.values(chartsImages).flat(); // 扁平化为图片 URL 列表
+  const imageMap = {};
+
+  for (const url of allUrls) {
+    if (typeof url !== "string" || !url.trim()) {
+      console.warn("⚠️ 非法图片 URL:", url);
+      continue;
+    }
+
+    try {
+      const buffer = await getImageFromUrl(url);
+      if (!buffer || buffer.length === 0) {
+        console.warn("⚠️ 获取的图片 buffer 长度为 0:", url);
+        continue;
+      }
+      imageMap[url] = buffer;
+    } catch (e) {
+      console.warn("⚠️ 加载图片失败:", url, e.message);
+    }
+  }
+
+  return imageMap;
+};
+
+// 生成 Word 文件
+export const copyFileDocx = async (fieldInfo, chartsImages) => {
+  try {
+    const sourceFilePath = path.join(
+      process.cwd(),
+      "src",
+      "public",
+      "file",
+      "副本XXX风电场可靠性和能效双提升数据分析报告(模板).docx"
+    );
+
+    const targetFilePath = path.join(
+      process.cwd(),
+      "src",
+      "public",
+      "file",
+      `${fieldInfo.companyName}${fieldInfo.fieldName}可靠性和能效双提升数据分析报告.docx`
+    );
+
+    // 拷贝模板
+    await fs.copy(sourceFilePath, targetFilePath);
+    const content = await fs.readFile(targetFilePath, "binary");
+    const zip = new PizZip(content);
+
+    // 预加载所有图片
+    const imageBufferMap = await preloadAllImages(chartsImages);
+
+    // 配置 image module
+    const imageModule = new ImageModule({
+      centered: true,
+      getImage: (tagValue) => {
+        const buffer = imageBufferMap[tagValue];
+        if (!buffer) {
+          throw new Error(`未找到图片 Buffer: ${tagValue}`);
+        }
+        return buffer;
+      },
+
+      getSize: (imgBuffer) => {
+        try {
+          const dimensions = sizeOf(imgBuffer);
+          const maxWidth = 500;
+          const ratio = maxWidth / dimensions.width;
+          return [maxWidth, dimensions.height * ratio];
+        } catch (e) {
+          console.error("❌ 获取图片尺寸失败", e);
+          console.error("buffer 长度:", imgBuffer.length);
+          throw new Error("图片尺寸获取失败,可能是非法图片或 Buffer 损坏");
+        }
+      },
+    });
+
+    const doc = new Docxtemplater(zip, {
+      paragraphLoop: true,
+      linebreaks: true,
+      modules: [imageModule],
+    });
+
+    const now = new Date();
+    const year = now.getFullYear();
+    const month = String(now.getMonth() + 1).padStart(2, "0");
+
+    // 构建模板图像字段对象
+    const imageTagData = {};
+    for (const [tag, urlList] of Object.entries(chartsImages)) {
+      if (Array.isArray(urlList)) {
+        imageTagData[tag] = urlList.map((url) => ({ image: url }));
+      } else {
+        console.warn(`⚠️ 无效的 urlList: ${tag}`, urlList);
+        imageTagData[tag] = []; // 或 throw error
+      }
+    }
+    console.log(chartsImages.rows, "chartsImages");
+
+    const renderData = {
+      Year_now: year,
+      Month_now: month,
+      Province: fieldInfo.provinceName,
+      Wind_farm: fieldInfo.fieldName,
+      Wind_turbine: fieldInfo.engineMillTypes.join(","),
+      Overview_of_the_Wind_Farm: `${fieldInfo.fieldName}位于${
+        fieldInfo.provinceName
+      }${fieldInfo.cityName},海拔高度为${fieldInfo.elevationHeight} 米,经度${
+        fieldInfo.longitude
+      }°,纬度${fieldInfo.latitude}°。风场总容量为${
+        fieldInfo.ratedCapacityNumber
+      } MW,共安装${
+        fieldInfo.engineCount
+      } 台风机,机型包括${fieldInfo.engineMillTypes?.join(",")}`,
+      ...imageTagData, // 图像数据动态插入
+      rows: chartsImages.rows,
+      windTurbineRows: chartsImages.windTurbineRows,
+      faultRows: chartsImages.faultRows,
+      yawErrorRows: chartsImages.yawErrorRows,
+    };
+
+    // 渲染 Word 模板
+    doc.render(renderData);
+
+    // 保存生成的文档
+    const buffer = doc.getZip().generate({ type: "nodebuffer" });
+    await fs.writeFile(targetFilePath, buffer);
+
+    return {
+      url: targetFilePath,
+      message: `✅ 文档生成成功:${targetFilePath}`,
+    };
+  } catch (error) {
+    console.error("❌ 生成 Word 文档失败:", error);
+    throw error;
+  }
+};

+ 1 - 16
downLoadServer/src/server/utils/minioService.js

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2025-04-28 14:43:09
- * @LastEditTime: 2025-04-29 16:07:15
+ * @LastEditTime: 2025-05-14 13:42:40
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /downLoadServer/src/server/utils/minioService.js
@@ -38,18 +38,3 @@ export const uploadFileToMinIO = async (bucketName, filePath, objectName) => {
     console.error("Error uploading file to MinIO:", error);
   }
 };
-
-export const checkIfAFileExists = async (bucketName, objectName) => {
-  return minioClient
-    .statObject(bucketName, objectName)
-    .then((stat) => {
-      console.log("对象存在:", stat);
-    })
-    .catch((error) => {
-      if (error.code === "NoSuchKey") {
-        console.log("对象不存在");
-      } else {
-        console.error("发生错误:", error);
-      }
-    });
-};

+ 2 - 2
src/components/map/index.vue

@@ -248,12 +248,12 @@ export default {
       layers: [
         new TileLayer({
           source: new XYZ({
-            url: "http://106.120.102.238:18000/tiles/{z}/{x}/{y}.png", //外网
+            // url: "http://106.120.102.238:18000/tiles/{z}/{x}/{y}.png", //外网
             // url: "http://127.0.0.1:8010/tiles/{z}/{x}/{y}.png", //本地
             // url: "http://192.168.50.235/tiles/{z}/{x}/{y}.png", //内网
             // url: "http://10.96.137.5:9080/tiles/{z}/{x}/{y}.png", //大~#@唐
             // url: "http://192.168.0.1/tiles/{z}/{x}/{y}.png", //华电
-            // url: "http://192.168.50.235/tiles/{z}/{x}/{y}.png", //中广核
+            url: "http://192.168.50.235/tiles/{z}/{x}/{y}.png", //中广核
           }),
         }),
         new VectorLayer({

+ 25 - 0
src/utils/common.js

@@ -392,6 +392,30 @@ export const downLoadCsvFile = (csvContent, fileName) => {
   document.body.removeChild(link);
   URL.revokeObjectURL(url);
 };
+
+export const downloadDocx = (url, fileName) => {
+  axios({
+    url: url,
+    method: "GET",
+    responseType: "blob", // 将响应类型设置为 blob
+  })
+    .then((response) => {
+      const blob = new Blob([response.data], {
+        type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+      });
+      const link = document.createElement("a");
+      link.href = URL.createObjectURL(blob);
+      link.download = fileName || "download.docx";
+      document.body.appendChild(link);
+      link.click();
+      document.body.removeChild(link);
+      URL.revokeObjectURL(link.href); // 释放 Blob URL 资源
+    })
+    .catch((error) => {
+      console.error("下载失败:", error);
+    });
+};
+
 export default {
   uuid,
   copy,
@@ -409,4 +433,5 @@ export default {
   downLoadChartsJsonFile,
   creatNewChartsJson,
   downLoadCsvFile,
+  downloadDocx,
 };

+ 34 - 0
src/utils/downloadFile.js

@@ -0,0 +1,34 @@
+/*
+ * @Author: your name
+ * @Date: 2025-05-26 16:57:22
+ * @LastEditTime: 2025-05-26 16:57:40
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/utils/downloadFile.js
+ */
+// ... existing code ...
+import axios from "axios";
+export const downloadDocx = (url, fileName) => {
+  axios({
+    url: url,
+    method: "GET",
+    responseType: "blob", // 将响应类型设置为 blob
+  })
+    .then((response) => {
+      const blob = new Blob([response.data], {
+        type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+      });
+      const link = document.createElement("a");
+      link.href = URL.createObjectURL(blob);
+      link.download = fileName || "download.docx";
+      document.body.appendChild(link);
+      link.click();
+      document.body.removeChild(link);
+      URL.revokeObjectURL(link.href); // 释放 Blob URL 资源
+    })
+    .catch((error) => {
+      console.error("下载失败:", error);
+    });
+};
+
+// ... existing code ...

+ 0 - 1
src/views/home/Index.vue

@@ -84,7 +84,6 @@ export default {
       height: 100%;
     }
   }
-  
 }
 ::v-deep.el-header {
   padding: 0 !important;

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

@@ -1,7 +1,7 @@
 <!--
  * @Author: your name
  * @Date: 2025-01-13 13:56:55
- * @LastEditTime: 2025-03-21 14:39:23
+ * @LastEditTime: 2025-05-16 15:08:41
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/overview/components/fault_all/index.vue
@@ -305,15 +305,16 @@ export default {
                         .filter((row) => Object.keys(row).length)
                         .slice(0, result.data.length - 1),
                     });
-                  } else {
-                    //分机型故障统计处理
-                    this.fenFaultCsvHeader.push(Object.keys(result.data[0]));
-                    this.fenFaultCsvData.push({
-                      data: result.data
-                        .filter((row) => Object.keys(row).length)
-                        .slice(0, result.data.length - 1),
-                    });
                   }
+                  //  else {
+                  //   //分机型故障统计处理
+                  //   this.fenFaultCsvHeader.push(Object.keys(result.data[0]));
+                  //   this.fenFaultCsvData.push({
+                  //     data: result.data
+                  //       .filter((row) => Object.keys(row).length)
+                  //       .slice(0, result.data.length - 1),
+                  //   });
+                  // }
                 }
               },
               error: (error) => {

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

@@ -1,7 +1,7 @@
 <!--
  * @Author: your name
  * @Date: 2025-01-10 09:11:34
- * @LastEditTime: 2025-02-26 11:25:24
+ * @LastEditTime: 2025-05-13 09:44:37
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/overview/components/wind_speed_frequency/index.vue
@@ -37,6 +37,7 @@
         keyField="fieldEngineCode"
         :itemSize="452"
         v-slot="{ item, index }"
+        v-if="diagramRelationsDatas && diagramRelationsDatas.length > 0"
       >
         <BarChart
           :setUpImgData="setUpImgData"
@@ -59,8 +60,9 @@
           ></BarChart>
         </template>
       </div>
-      <el-empty description="暂无分析记录" v-else></el-empty> -->
-
+     
+      -->
+      <el-empty description="暂无分析记录" v-else></el-empty>
       <el-dialog
         v-if="isShowDescription"
         title="添加评论"

+ 2 - 1
src/views/overview/components/yaw_error_density/index.vue

@@ -235,7 +235,7 @@ export default {
           fieldEngineCodes:
             this.fieldEngineCodes.length === 0
               ? undefined
-              : this.fieldEngineCodes,
+              : this.fieldEngineCodes.join(","),
         });
         if (result.data.length > 0) {
           this.isShowDescription = true;
@@ -294,6 +294,7 @@ export default {
     getEnfineList(data, setUpImg) {
       this.fieldEngineCodes = data;
       this.setUpImgData = [...setUpImg];
+      console.log("动态偏航误差", this.fieldEngineCodes);
       this.getAnalysisDetail();
     },
     //下一条

+ 300 - 93
src/views/performance/assetssMag.vue

@@ -1,12 +1,12 @@
 <template>
   <div class="global-variable">
     <div class="condition">
-      <el-alert
+      <!-- <el-alert
         title="分析前请核对数据是否导入,点击查看是否导入即可查看"
         type="warning"
         show-icon
       >
-      </el-alert>
+      </el-alert> -->
       <el-form
         :inline="true"
         ref="ruleForm"
@@ -466,6 +466,7 @@ import {
   editPriority,
   analysisDetail,
 } from "@/api/performance";
+import { downloadDocx } from "@/utils/common";
 import { getFieldInfo } from "@/api/overview";
 import axios from "axios";
 import pLimit from "p-limit";
@@ -476,6 +477,7 @@ export default {
   },
   data() {
     return {
+      fileDataList: {},
       fieldInfo: {}, //风场信息
       firstLoad: false, // 是否是第一次加载
       fieldCodeList: [],
@@ -546,92 +548,129 @@ export default {
   },
   methods: {
     // async handleDownLoadChart(row) {
-    //   //请求该批次下全部分析类型
+    //   console.log(row.fieldCode, "row");
     //   await this.getAllAnalysis(row.batchCode);
-    //   //获取风场信息 要在pdf中展示
     //   await this.getFieldDetail(row.batchCode);
-    //   //循环分析类型分片逻辑处理
-    //   this.allAnalysis.map(async (itemAnalysis, indAnalysis) => {
-    //     const filterAnalysis = allAnalysisType.filter(
-    //       (itemType) => itemType.typeCode === itemAnalysis.analysisTypeCode
+
+    //   const diagramRelations = [
+    //     {
+    //       batchCode: "WOF039800012-WOB000001",
+    //       fieldEngineCode: "WOG00604",
+    //       fieldEngineName: "#01",
+    //       analysisTypeCode: "power_curve",
+    //       analysisTypeName: null,
+    //       fileAddr:
+    //         "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/power_curve/manual/power_curve-%2301.json",
+    //       autoAnalysis: null,
+    //       createTime: "2025-05-23 16:57:26",
+    //     },
+    //     {
+    //       batchCode: "WOF039800012-WOB000001",
+    //       fieldEngineCode: "WOG00604",
+    //       fieldEngineName: "#01",
+    //       analysisTypeCode: "power_curve",
+    //       analysisTypeName: null,
+    //       fileAddr:
+    //         "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/power_curve/manual/power_curve-%2301.json",
+    //       autoAnalysis: null,
+    //       createTime: "2025-05-23 16:57:26",
+    //     },
+    //     {
+    //       batchCode: "WOF039800012-WOB000001",
+    //       fieldEngineCode: "WOG00608",
+    //       fieldEngineName: "#05",
+    //       analysisTypeCode: "power_curve",
+    //       analysisTypeName: null,
+    //       fileAddr:
+    //         "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/power_curve/manual/power_curve-%2305.json",
+    //       autoAnalysis: null,
+    //       createTime: "2025-05-23 16:57:26",
+    //     },
+    //     {
+    //       batchCode: "WOF039800012-WOB000001",
+    //       fieldEngineCode: "WOG00613",
+    //       fieldEngineName: "#10",
+    //       analysisTypeCode: "power_curve",
+    //       analysisTypeName: null,
+    //       fileAddr:
+    //         "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/power_curve/manual/power_curve-%2310.json",
+    //       autoAnalysis: null,
+    //       createTime: "2025-05-23 16:57:26",
+    //     },
+    //   ];
+    //   diagramRelations.map(async (item) => {
+    //     const res = await axios.post(
+    //       "/downLoadChart/chartServer/charts/PlotlyChartsFen",
+    //       {
+    //         fieldEngineCode: item.fieldEngineCode ? item.fieldEngineCode : "", //lineChartFen
+    //         bucketName: "bucket-zhzn",
+    //         // row.fieldCode + "/" + row.batchCode + "/" + item.analysisTypeCode, //桶名称
+    //         objectName: "charts/111.jpg", //在 MinIO 中的文件名
+    //         fieldInfo: this.fieldInfo,
+    //         fileAddr: item.fileAddr,
+    //         // chartType: "radar",
+    //         chartType: "",
+    //         // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/wind_speed_frequency/manual/wind_Speed_Frequency%2302.json", //barChart  url:bar
+    //         // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/cp_windspeed/manual/%2319.json", //lineChartFen  url:line
+    //         // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/cp/manual/WEM00013.json", //lineAndChartLine  url:lineAndChildLine
+    //         // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/data_integrity_second/manual/Data_Integrity_Of_Second_Analyst.json", //HeatmapCharts  url:heatmap
+    //         // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/speed_torque/manual/total_3D_WEM00012.json", //3DDrawingChart  url:3DDrawingChart
+    //         // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/speed_torque/manual/3D_%2301.json", //Time3DChart  url:Time3DChart
+    //         // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/tsr_cp_power_scatter/manual/%2301.json", //TwoDMarkersChart  url:TwoDMarkersChart
+    //         // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/power_scatter_2D/manual/%2301-scatter.json", //TwoDMarkersChart1  url:TwoDMarkersChart1 2D有功功率散点图
+    //         // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/min_pitch/manual/min_pitch%2301.json",
+    //         // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/yaw_error_density/manual/%2304.json", //ColorbarInitTwoDmarkersChart  url:ColorbarInitTwoDmarkersChart 最小桨距角 、动态偏航误差
+    //         // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/pitch_generator_speed/manual/pitch_GeneratorSpeed_Analyst%2301.json", //powerMarkers2DCharts url:powerMarkers2DCharts
+    //         // "", //radarChart url:radarChart
+    //         // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/rated_power_windspeed/manual/total_less_25.json", //boxLineCharts
+    //         // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/tsr_trend/manual/%2302.json", //boxMarkersCharts
+    //         // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/temperature_large_components/manual/GeneratorTemperature/%2321.json", //generatorTemperature
+    //         // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/wind_direction_frequency/manual/wind_direction_frequency%2318.json", //windRoseChart
+    //         // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/yaw_error/manual/%2320.json", //yawErrorLine
+    //         // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/fault/manual/total_fault_result.csv", //faultAllChart //全场故障
+    //         // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/fault/manual/turbine_fault_result.csv", //faultUnitChart //机组故障
+    //         // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/yaw_error/manual/yaw_error_result.csv", //静态偏航总图 //yawErrorBarSumChart
+    //         // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/production_indicator/manual/production_indicator_total.csv", //全场指标 productionIndicatorTotal
+    //       }
     //     );
-    //     itemAnalysis.generalFiles &&
-    //       itemAnalysis.generalFiles.map(async (itemField, indField) => {
-    //         const res = await axios.post(
-    //           "/downLoadChart/chartServer/charts/" +
-    //             filterAnalysis[0].generalFiles.urlType,
-    //           {
-    //             fieldEngineCode: "WOG00623", //lineChartFen
-    //             bucketName:
-    //               row.fieldCode +
-    //               "/" +
-    //               row.batchCode +
-    //               "/" +
-    //               itemAnalysis.analysisTypeCode, //桶名称
-    //             objectName:
-    //               itemField.fieldEngineName || itemField.engineTypeCode, //在 MinIO 中的文件名
-    //             fieldInfo: this.fieldInfo,
-    //             fileAddr: itemField.fileAddr,
-    //             // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/wind_speed_frequency/manual/wind_Speed_Frequency%2302.json", //barChart  url:bar
-    //             // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/cp/manual/%2320.json", //lineChartFen  url:line
-    //             // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/cp/manual/WEM00013.json", //lineAndChartLine  url:lineAndChildLine
-    //             // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/data_integrity_second/manual/Data_Integrity_Of_Second_Analyst.json", //HeatmapCharts  url:heatmap
-    //             // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/speed_torque/manual/total_3D_WEM00012.json", //3DDrawingChart  url:3DDrawingChart
-    //             // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/speed_torque/manual/3D_%2301.json", //Time3DChart  url:Time3DChart
-    //             // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/tsr_cp_power_scatter/manual/%2301.json", //TwoDMarkersChart  url:TwoDMarkersChart
-    //             // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/power_scatter_2D/manual/%2301-scatter.json", //TwoDMarkersChart1  url:TwoDMarkersChart1 2D有功功率散点图
-    //             // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/min_pitch/manual/min_pitch%2301.json",
-    //             // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/yaw_error_density/manual/%2304.json", //ColorbarInitTwoDmarkersChart  url:ColorbarInitTwoDmarkersChart 最小桨距角 、动态偏航误差
-    //             // "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/pitch_generator_speed/manual/pitch_GeneratorSpeed_Analyst%2301.json", //powerMarkers2DCharts url:powerMarkers2DCharts
-    //             // "", //radarChart url:radarChart
-    //           }
-    //         );
-    //         console.log(res, "res linechart");
-    //       });
-    //     itemAnalysis.diagramRelations &&
-    //       itemAnalysis.diagramRelations.map(async (itemField, indField) => {
-    //         const res = await axios.post(
-    //           "/downLoadChart/chartServer/charts/" +
-    //             Array.isArray(filterAnalysis[0].diagramRelations.urlType)
-    //             ? this.getFileTypeFromUrl(
-    //                 filterAnalysis[0].diagramRelations.FileTypeFromUrl
-    //               ) === filterAnalysis[0].diagramRelations.FileTypeFromUrl
-    //               ? filterAnalysis[0].diagramRelations.urlType[0]
-    //               : filterAnalysis[0].diagramRelations.urlType[1]
-    //             : filterAnalysis[0].diagramRelations.urlType,
-    //           {
-    //             fieldEngineCode: "WOG00623", //lineChartFen
-    //             bucketName:
-    //               row.fieldCode +
-    //               "/" +
-    //               row.batchCode +
-    //               "/" +
-    //               itemAnalysis.analysisTypeCode, //桶名称
-    //             objectName:
-    //               itemField.fieldEngineName || itemField.engineTypeCode, //在 MinIO 中的文件名
-    //             fieldInfo: this.fieldInfo,
-    //             fileAddr: itemField.fileAddr,
-    //           }
-    //         );
-    //         console.log(res, "res linechart");
-    //       });
     //   });
+
+    //   // await axios.post("/downLoadChart/chartServer/charts/CopyFileCsv", {
+    //   //   fieldInfo: this.fieldInfo,
+    //   //   rows: [
+    //   //     {
+    //   //       Qdr: "-70352582.72",
+    //   //       Qp: "70352582.72",
+    //   //       Rdr: "0",
+    //   //       Thc: "1675.06",
+    //   //     },
+    //   //   ],
+    //   //   "zn-techcn-replace-tags-production_indicator_unit-generalFiles": [
+    //   //     "http://192.168.50.233:6900/bucket-zhzn/charts/#03.jpg",
+    //   //     "http://192.168.50.233:6900/bucket-zhzn/charts/#05.jpg",
+    //   //     "http://192.168.50.233:6900/bucket-zhzn/charts/#06.jpg",
+    //   //     "http://192.168.50.233:6900/bucket-zhzn/charts/#08.jpg",
+    //   //     "http://192.168.50.233:6900/bucket-zhzn/charts/#09.jpg",
+    //   //     "http://192.168.50.233:6900/bucket-zhzn/charts/#14.jpg",
+    //   //     "http://192.168.50.233:6900/bucket-zhzn/charts/#17.jpg",
+    //   //   ],
+    //   // });
     // },
-    //获取 风机信息
+
+    // 获取 风机信息
+
     async handleDownLoadChart(row) {
       await this.getAllAnalysis(row.batchCode);
       await this.getFieldDetail(row.batchCode);
-
+      this.$message.info("开始生成word文档");
       const limit = pLimit(5); // 限制同时并发的请求数量为 5
       const tasks = [];
-
       for (const itemAnalysis of this.allAnalysis) {
         const filterAnalysis = allAnalysisType.filter(
-          (itemType) => itemType.typeCode === itemAnalysis.analysisTypeCode
+          (itemType) => itemType.typeName === itemAnalysis.analysisTypeName
         )[0];
-
         if (itemAnalysis.generalFiles) {
-          //这里过滤的是发电机温度的类型 在url 中是否能找到filterFileAddr 关键字
+          //这里过滤的是发电机温度的类型 在url 中是否能找到filterFileAddr
           for (const itemField of filterAnalysis.filterFileAddr
             ? itemAnalysis.generalFiles
                 .filter((item) => item.fileAddr.endsWith(".json"))
@@ -639,19 +678,105 @@ export default {
                   item.fileAddr.includes(filterAnalysis.filterFileAddr)
                 ) || []
             : itemAnalysis.generalFiles) {
-            tasks.push(
-              limit(() =>
-                this.postChartData(
-                  filterAnalysis.generalFiles.urlType,
-                  row,
-                  itemAnalysis,
-                  itemField
+            //静态偏航误差 --通过allAnalysisType.js文件配置实现需求
+            if (Array.isArray(filterAnalysis.generalFiles.urlType)) {
+              filterAnalysis.generalFiles.urlType.map(
+                (itemUrlType, indUrlType) => {
+                  tasks.push(
+                    limit(() =>
+                      this.postChartData(
+                        itemUrlType,
+                        row,
+                        itemAnalysis,
+                        itemField,
+                        filterAnalysis,
+                        "generalFiles"
+                      )
+                    )
+                  );
+                }
+              );
+            } else if (filterAnalysis.typeCode === "fault") {
+              if (itemField.fileAddr.includes("turbine_fault_result")) {
+                if (itemField.engineTypeCode === "turbine_fault_result") {
+                  tasks.push(
+                    limit(() =>
+                      this.postChartData(
+                        "faultUnitChart",
+                        row,
+                        itemAnalysis,
+                        itemField,
+                        filterAnalysis,
+                        "generalFiles"
+                      )
+                    )
+                  );
+                }
+              } else {
+                if (itemField.engineTypeCode === "total_fault_result") {
+                  tasks.push(
+                    limit(() =>
+                      this.postChartData(
+                        "faultAllChart",
+                        row,
+                        itemAnalysis,
+                        itemField,
+                        filterAnalysis,
+                        "generalFiles"
+                      )
+                    )
+                  );
+                }
+              }
+            } else if (filterAnalysis.typeCode === "production_indicator") {
+              //总图全场
+              if (
+                itemField.fileAddr.includes(
+                  filterAnalysis.generalFiles.FileTypeFromUrl
                 )
-              )
-            );
+              ) {
+                tasks.push(
+                  limit(() =>
+                    this.postChartData(
+                      filterAnalysis.generalFiles.urlType,
+                      row,
+                      itemAnalysis,
+                      itemField,
+                      filterAnalysis,
+                      "generalFiles"
+                    )
+                  )
+                );
+              } else {
+                tasks.push(
+                  limit(() =>
+                    this.postChartData(
+                      "radarChart",
+                      row,
+                      itemAnalysis,
+                      itemField,
+                      filterAnalysis,
+                      "generalFiles"
+                    )
+                  )
+                );
+              }
+            } else {
+              tasks.push(
+                limit(() =>
+                  this.postChartData(
+                    filterAnalysis.generalFiles.urlType,
+                    row,
+                    itemAnalysis,
+                    itemField,
+                    filterAnalysis,
+                    "generalFiles"
+                  )
+                )
+              );
+            }
           }
         }
-
         if (itemAnalysis.diagramRelations) {
           for (const itemField of filterAnalysis.filterFileAddr
             ? itemAnalysis.diagramRelations
@@ -670,38 +795,120 @@ export default {
                 ? filterAnalysis.diagramRelations.urlType[0]
                 : filterAnalysis.diagramRelations.urlType[1]
               : filterAnalysis.diagramRelations.urlType;
-
+            // console.log("urlType", filterAnalysis.typeName, urlType);
             tasks.push(
               limit(() =>
-                this.postChartData(urlType, row, itemAnalysis, itemField)
+                this.postChartData(
+                  urlType,
+                  row,
+                  itemAnalysis,
+                  itemField,
+                  filterAnalysis,
+                  "diagramRelations"
+                )
               )
             );
           }
         }
       }
-
       await Promise.all(tasks);
+
+      const wordFilePath = await axios.post(
+        "/downLoadChart/chartServer/charts/CopyFileCsv",
+        {
+          fieldInfo: this.fieldInfo,
+          bucketName: "bucket-zhzn",
+          objectName: `charts/${row.fieldCode}/${row.batchCode}`,
+          ...this.fileDataList,
+        }
+      );
+      // //下载minio 文件
+      downloadDocx(wordFilePath.data.data.url, wordFilePath.data.data.fileName);
+
+      console.log("生成成功:", this.fileDataList);
       console.log("全部图表生成请求已完成");
     },
 
-    async postChartData(urlType, row, itemAnalysis, itemField) {
+    async postChartData(
+      urlType,
+      row,
+      itemAnalysis,
+      itemField,
+      filterAnalysis,
+      typeChart
+    ) {
       try {
+        const objectname =
+          itemAnalysis.analysisTypeCode === "temperature_large_components"
+            ? filterAnalysis.filterFileAddr +
+              "/" +
+              itemField.fieldEngineName +
+              ".jpg"
+            : urlType + itemField.fieldEngineName + ".jpg" ||
+              urlType + itemField.engineTypeCode + ".jpg";
         const res = await axios.post(
           `/downLoadChart/chartServer/charts/${urlType}`,
           {
-            fieldEngineCode: "WOG00623",
-            bucketName: `${row.fieldCode}/${row.batchCode}/${itemAnalysis.analysisTypeCode}`,
-            objectName: itemField.fieldEngineName || itemField.engineTypeCode,
+            fieldEngineCode: itemField.fieldEngineCode,
+            bucketName: "bucket-zhzn",
+            objectName:
+              `charts/${row.fieldCode}/${row.batchCode}/${itemAnalysis.analysisTypeCode}/` +
+              objectname,
             fieldInfo: this.fieldInfo,
             fileAddr: itemField.fileAddr,
+            chartType:
+              filterAnalysis.typeDocxName === "production_indicator_unit"
+                ? "radar"
+                : urlType,
           }
         );
-        console.log("生成成功:", res);
+        let key = `zn-techcn-replace-tags-${filterAnalysis.typeDocxName}-${typeChart}`;
+        if (urlType === "yawErrorBarSumChart") {
+          key = `zn-techcn-replace-tags-${filterAnalysis.typeDocxName}-generalFiles2`;
+
+          if (!this.fileDataList["yawErrorRows"]) {
+            this.$set(this.fileDataList, "yawErrorRows", []); // Vue 2 中响应式设置对象属性
+          }
+          this.fileDataList["yawErrorRows"] = res?.data?.data?.data;
+        } else if (urlType === "yawErrorChart") {
+          key = `zn-techcn-replace-tags-${filterAnalysis.typeDocxName}-generalFiles1`;
+        }
+        if (filterAnalysis.typeDocxName === "production_indicator_all") {
+          if (!this.fileDataList["rows"]) {
+            this.$set(this.fileDataList, "rows", []); // Vue 2 中响应式设置对象属性
+          }
+          this.fileDataList["rows"] = res?.data?.data?.data;
+        }
+        //
+        if (itemField.engineTypeCode === "total_fault_result") {
+          if (!this.fileDataList["faultRows"]) {
+            this.$set(this.fileDataList, "faultRows", []); // Vue 2 中响应式设置对象属性
+          }
+          this.fileDataList["faultRows"] = res?.data?.data?.data;
+        }
+        if (itemField.engineTypeCode === "turbine_fault_result") {
+          if (!this.fileDataList["windTurbineRows"]) {
+            this.$set(this.fileDataList, "windTurbineRows", []); // Vue 2 中响应式设置对象属性
+          }
+          this.fileDataList["windTurbineRows"] = res?.data?.data?.data;
+        }
+        if (filterAnalysis.typeDocxName === "production_indicator_unit") {
+          if (!this.fileDataList[key]) {
+            this.$set(this.fileDataList, key, []); // Vue 2 中响应式设置对象属性
+          }
+          res?.data?.data?.imageUrls.map((imgval) => {
+            this.fileDataList[key].push(imgval);
+          });
+        } else {
+          if (!this.fileDataList[key]) {
+            this.$set(this.fileDataList, key, []); // Vue 2 中响应式设置对象属性
+          }
+          this.fileDataList[key].push(res?.data?.data?.imageUrl);
+        }
       } catch (err) {
         console.error("生成失败:", err);
       }
     },
-
     getFileTypeFromUrl(url, keyword) {
       return url.includes(keyword) ? keyword : "Unknown";
     },

+ 7 - 2
src/views/performance/components/PlotlyCharts.vue

@@ -195,9 +195,11 @@ export default {
 
       const data = this.powerCurveData.turbines.map((turbine, index) => ({
         x: turbine.xData,
-        y: processNullValues(turbine.yData),
+        // y: processNullValues(turbine.yData),
+        y: turbine.yData,
         mode: "lines",
         name: turbine.enginName,
+        connectgaps: false,
         fill: this.chartType === "line" ? "none" : "tonexty",
         line: {
           color:
@@ -308,8 +310,10 @@ export default {
 
           const trace = {
             x: turbine.xData,
-            y: turbine.yData.map((val) => (val !== null ? val : 0.0)),
+            y: turbine.yData,
+            // y: turbine.yData.map((val) => (val !== null ? val : 0.0)),
             mode: "lines",
+            // connectgaps: false,
             name: turbine.enginName,
             fill: this.chartType === "line" ? "none" : "tonexty",
             line: {
@@ -338,6 +342,7 @@ export default {
             color: "red",
             width: 1, // 设置线条的宽度为1
           },
+          // connectgaps: false,
           marker: { color: "red", size: 4 },
           hovertemplate:
             `风速: %{x} m/s<br>合同功率: %{y} kW<br>` +

+ 78 - 82
src/views/performance/components/chartsCom/BoxMarkersCharts.vue

@@ -89,7 +89,6 @@ export default {
     onDateRangeChange() {
       this.drawBoxPlot(); // 日期变化时重新绘制图表
     },
-
     // 判断时间戳是否在选择的日期范围内
     isInDateRange(timestamp) {
       const [startDate, endDate] = this.dateRange;
@@ -98,7 +97,6 @@ export default {
       const date = new Date(timestamp);
       return date >= new Date(startDate) && date <= new Date(endDate);
     },
-
     // 过滤数据
     filterData(group) {
       const filteredXData = [];
@@ -124,93 +122,91 @@ export default {
         medians: filteredMedians,
       };
     },
-
     // 绘制箱线图
     drawBoxPlot() {
-  const chartContainer = this.$refs[`plotlyChart-${this.index}`];
-  const { data, xaixs, yaixs, analysisTypeCode } = this.chartData;
-
-  const filteredData = data.map(this.filterData);
-
-  const traces = [];
-  const medianMarkers = [];
-
-  filteredData.forEach((group) => {
-    traces.push({
-      x: group.xData,
-      y: group.yData,
-      type: 'box',
-      name: group.title,
-      marker: { color: '#263649' },
-      line: { width: 0.8 },
-      fillcolor: '#458EF7',
-      boxpoints: false, // 不显示异常值
-      boxmean: true, // 显示均值
-      hovertemplate:
-        `<b>${xaixs}</b>: %{x}<br>` +
-        `<b>最大值</b>: %{upperfence}<br>` +
-        `<b>上四分位数 (Q3)</b>: %{q3}<br>` +
-        `<b>中位数 (Median)</b>: %{median}<br>` +
-        `<b>下四分位数 (Q1)</b>: %{q1}<br>` +
-        `<b>最小值</b>: %{lowerfence}<br>` +
-        `<b>均值</b>: %{mean}<br>` +
-        `<extra></extra>`,
-    });
-
-    if (group.medians && group.medians.x.length > 0) {
-      medianMarkers.push({
-        x: group.medians.x,
-        y: group.medians.y,
-        mode: 'markers',
-        marker: { color: '#f00', size: 3 },
-        name: `${group.title} - 中位点`,
-        type: 'scatter',
-        hovertemplate: `<b>${xaixs}</b>: %{x} <br> <b>${yaixs}</b>: %{y} <br><b>中位点</b>: %{y}<br><extra></extra>`,
+      const chartContainer = this.$refs[`plotlyChart-${this.index}`];
+      const { data, xaixs, yaixs, analysisTypeCode } = this.chartData;
+
+      const filteredData = data.map(this.filterData);
+
+      const traces = [];
+      const medianMarkers = [];
+
+      filteredData.forEach((group) => {
+        traces.push({
+          x: group.xData,
+          y: group.yData,
+          type: "box",
+          name: group.title,
+          marker: { color: "#263649" },
+          line: { width: 0.8 },
+          fillcolor: "#458EF7",
+          boxpoints: false, // 不显示异常值
+          boxmean: true, // 显示均值
+          hovertemplate:
+            `<b>${xaixs}</b>: %{x}<br>` +
+            `<b>最大值</b>: %{upperfence}<br>` +
+            `<b>上四分位数 (Q3)</b>: %{q3}<br>` +
+            `<b>中位数 (Median)</b>: %{median}<br>` +
+            `<b>下四分位数 (Q1)</b>: %{q1}<br>` +
+            `<b>最小值</b>: %{lowerfence}<br>` +
+            `<b>均值</b>: %{mean}<br>` +
+            `<extra></extra>`,
+        });
+
+        if (group.medians && group.medians.x.length > 0) {
+          medianMarkers.push({
+            x: group.medians.x,
+            y: group.medians.y,
+            mode: "markers",
+            marker: { color: "#f00", size: 3 },
+            name: `${group.title} - 中位点`,
+            type: "scatter",
+            hovertemplate: `<b>${xaixs}</b>: %{x} <br> <b>${yaixs}</b>: %{y} <br><b>中位点</b>: %{y}<br><extra></extra>`,
+          });
+        }
       });
-    }
-  });
 
-  const layout = {
-    title: {
-      text: analysisTypeCode + data[0].engineName,
-      font: { size: 16, weight: 'bold' },
-    },
-    xaxis: {
-      title: xaixs,
-      type: 'date',
-      tickformat: '%Y-%m-%d',
-      gridcolor: 'rgb(255,255,255)',
-      tickcolor: 'rgb(255,255,255)',
-      backgroundcolor: '#e5ecf6',
-    },
-    yaxis: {
-      title: yaixs,
-      gridcolor: 'rgb(255,255,255)',
-      tickcolor: 'rgb(255,255,255)',
-      backgroundcolor: '#e5ecf6',
-    },
-    plot_bgcolor: '#e5ecf6',
-    showlegend: true,
-  };
-
-  const getChartSetUp = (axisTitle) => {
-    return this.setUpImgData.find((item) => item.text.includes(axisTitle));
-  };
+      const layout = {
+        title: {
+          text: analysisTypeCode + data[0].engineName,
+          font: { size: 16, weight: "bold" },
+        },
+        xaxis: {
+          title: xaixs,
+          type: "date",
+          tickformat: "%Y-%m-%d",
+          gridcolor: "rgb(255,255,255)",
+          tickcolor: "rgb(255,255,255)",
+          backgroundcolor: "#e5ecf6",
+        },
+        yaxis: {
+          title: yaixs,
+          gridcolor: "rgb(255,255,255)",
+          tickcolor: "rgb(255,255,255)",
+          backgroundcolor: "#e5ecf6",
+        },
+        plot_bgcolor: "#e5ecf6",
+        showlegend: true,
+      };
 
-  const xChartSetUp = getChartSetUp(layout.xaxis.title);
-  if (xChartSetUp) {
-    layout.xaxis.dtick = xChartSetUp.dtick;
-    layout.xaxis.range = [xChartSetUp.min, xChartSetUp.max];
-  }
-  const yChartSetUp = getChartSetUp(layout.yaxis.title);
-  if (yChartSetUp) {
-    layout.yaxis.dtick = yChartSetUp.dtick;
-    layout.yaxis.range = [yChartSetUp.min, yChartSetUp.max];
-  }
+      const getChartSetUp = (axisTitle) => {
+        return this.setUpImgData.find((item) => item.text.includes(axisTitle));
+      };
 
-  Plotly.newPlot(chartContainer, [...traces, ...medianMarkers], layout);
-},
+      const xChartSetUp = getChartSetUp(layout.xaxis.title);
+      if (xChartSetUp) {
+        layout.xaxis.dtick = xChartSetUp.dtick;
+        layout.xaxis.range = [xChartSetUp.min, xChartSetUp.max];
+      }
+      const yChartSetUp = getChartSetUp(layout.yaxis.title);
+      if (yChartSetUp) {
+        layout.yaxis.dtick = yChartSetUp.dtick;
+        layout.yaxis.range = [yChartSetUp.min, yChartSetUp.max];
+      }
 
+      Plotly.newPlot(chartContainer, [...traces, ...medianMarkers], layout);
+    },
   },
 };
 </script>

+ 1 - 115
src/views/performance/components/chartsCom/FaultAll.vue

@@ -1,7 +1,7 @@
 <!--
  * @Author: your name
  * @Date: 2025-01-15 14:24:59
- * @LastEditTime: 2025-03-14 19:05:37
+ * @LastEditTime: 2025-05-16 11:14:32
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/performance/components/chartsCom/FaultAll.vue
@@ -148,120 +148,6 @@ export default {
       // 渲染图表
       Plotly.newPlot(this.$refs.chart, [barTrace, lineTrace], layout);
     },
-
-    // renderChart() {
-    //   var trace1 = {
-    //     x: [
-    //       "Jan",
-    //       "Feb",
-    //       "Mar",
-    //       "Apr",
-    //       "May",
-    //       "Jun",
-    //       "Jul",
-    //       "Aug",
-    //       "Sep",
-    //       "Oct",
-    //       "Nov",
-    //       "Dec",
-    //     ],
-    //     y: [
-    //       2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3,
-    //     ],
-    //     type: "bar",
-    //     name: "Evaporation",
-    //     marker: { color: "#5470C6" },
-    //   };
-
-    //   var trace2 = {
-    //     x: [
-    //       "Jan",
-    //       "Feb",
-    //       "Mar",
-    //       "Apr",
-    //       "May",
-    //       "Jun",
-    //       "Jul",
-    //       "Aug",
-    //       "Sep",
-    //       "Oct",
-    //       "Nov",
-    //       "Dec",
-    //     ],
-    //     y: [
-    //       2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3,
-    //     ],
-    //     type: "bar",
-    //     name: "Precipitation",
-    //     yaxis: "y2", // 使用第二个 Y 轴
-    //     marker: { color: "#91CC75" },
-    //   };
-
-    //   var trace3 = {
-    //     x: [
-    //       "Jan",
-    //       "Feb",
-    //       "Mar",
-    //       "Apr",
-    //       "May",
-    //       "Jun",
-    //       "Jul",
-    //       "Aug",
-    //       "Sep",
-    //       "Oct",
-    //       "Nov",
-    //       "Dec",
-    //     ],
-    //     y: [2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3, 23.4, 23.0, 16.5, 12.0, 6.2],
-    //     type: "scatter",
-    //     mode: "lines+markers",
-    //     name: "Temperature",
-    //     line: { color: "#EE6666" },
-    //     yaxis: "y3", // 使用第三个 Y 轴
-    //   };
-
-    //   var layout = {
-    //     title: "Evaporation, Precipitation, and Temperature",
-    //     xaxis: {
-    //       title: "Month",
-    //     },
-    //     yaxis: {
-    //       title: "Evaporation",
-    //       titlefont: { color: "#5470C6" },
-    //       tickfont: { color: "#5470C6" },
-    //       side: "left",
-    //       showline: true,
-    //       linecolor: "#5470C6",
-    //     },
-    //     yaxis2: {
-    //       title: "Precipitation",
-    //       titlefont: { color: "#91CC75" },
-    //       tickfont: { color: "#91CC75" },
-    //       overlaying: "y",
-    //       side: "right",
-    //       showline: true,
-    //       linecolor: "#91CC75",
-    //     },
-    //     yaxis3: {
-    //       title: "Temperature (°C)",
-    //       titlefont: { color: "#EE6666" },
-    //       tickfont: { color: "#EE6666" },
-    //       overlaying: "y",
-    //       side: "right",
-    //       position: 0.85, // 调整右侧 Y 轴的位置
-    //       showline: true,
-    //       linecolor: "#EE6666",
-    //     },
-    //     showlegend: true,
-    //     margin: {
-    //       t: 50,
-    //       b: 50,
-    //       r: 100,
-    //     },
-    //   };
-
-    //   Plotly.newPlot(this.$refs.chart, [trace1, trace2, trace3], layout);
-    // },
   },
 };
 </script>

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

@@ -1,11 +1,3 @@
-<!--
- * @Author: your name
- * @Date: 2025-01-15 15:49:57
- * @LastEditTime: 2025-04-11 16:55:00
- * @LastEditors: bogon
- * @Description: In User Settings Edit
- * @FilePath: /performance-test/src/views/performance/components/chartsCom/FaultUntal.vue
--->
 <template>
   <div>
     <div ref="chart" style="width: 100%; height: 400px"></div>

+ 1 - 1
src/views/performance/components/chartsCom/HeatmapCharts.vue

@@ -92,7 +92,6 @@ export default {
           this.initData[0].x = this.chartData.data[0].xData;
           this.initData[0].y = this.chartData.data[0].yData;
           this.initData[0].z = this.chartData.data[0].ZData;
-
           nextTick(() => {
             this.initcharts();
           });
@@ -138,6 +137,7 @@ export default {
           title: this.chartData.xaixs,
           tickvals: this.initData[0].x,
           ticktext: this.initData[0].x,
+          type: "category",
           tickmode: "array",
         },
         yaxis: {

+ 1 - 0
src/views/performance/components/chartsCom/WindRoseChart.vue

@@ -174,6 +174,7 @@ export default {
             gridcolor: "rgb(255,255,255)",
             tickcolor: "rgb(255,255,255)",
             linecolor: "rgb(255,255,255)",
+            showticklabels: false, // 隐藏角度标签
           },
         },
         showlegend: true,

+ 1 - 1
src/views/performance/components/chartsCom/yawErrorBarSum.vue

@@ -148,7 +148,7 @@ export default {
           b: 50,
         },
         autosize: true, // 开启自适应
-        showlegend: true, // 显示图例
+        // showlegend: true, // 显示图例
         plot_bgcolor: "#e5ecf6",
         gridcolor: "#fff",
         bgcolor: "#e5ecf6", // 设置背景颜色

+ 7 - 7
src/views/performance/components/chartsCom/yawErrorLine.vue

@@ -1,7 +1,7 @@
 <!--
  * @Author: your name
  * @Date: 2025-01-22 09:42:59
- * @LastEditTime: 2025-03-13 17:11:47
+ * @LastEditTime: 2025-05-14 11:19:18
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/performance/components/chartsCom/yawErrorLine.vue
@@ -201,12 +201,12 @@ export default {
           backgroundcolor: "#e5ecf6",
           title: this.chartData.yaixs || "Y轴", // 纵坐标标题
         },
-        margin: {
-          l: 50,
-          r: 50,
-          t: 50,
-          b: 50,
-        },
+        // margin: {
+        //   l: 50,
+        //   r: 50,
+        //   t: 50,
+        //   b: 50,
+        // },
         plot_bgcolor: "#e5ecf6",
         gridcolor: "#fff",
         bgcolor: "#e5ecf6", // 设置背景颜色

+ 66 - 22
src/views/performance/js/allAnalysisType.js

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2025-05-06 17:01:14
- * @LastEditTime: 2025-05-08 16:07:23
+ * @LastEditTime: 2025-05-26 09:20:04
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/performance/js/allAnalysisType.js
@@ -12,6 +12,7 @@ export const allAnalysisType = [
     id: 101,
     parentId: 1,
     typeCode: "data_integrity_minute",
+    typeDocxName: "data_integrity_minute",
     typeFlag: "minute",
     generalFiles: {
       urlType: "heatmap",
@@ -25,6 +26,7 @@ export const allAnalysisType = [
     id: 102,
     parentId: 1,
     typeCode: "data_integrity_second",
+    typeDocxName: "data_integrity_second",
     typeFlag: "second",
     generalFiles: {
       urlType: "heatmap",
@@ -38,6 +40,7 @@ export const allAnalysisType = [
     id: 103,
     parentId: 2,
     typeCode: "wind_speed_frequency",
+    typeDocxName: "wind_speed_frequency",
     typeFlag: "minute",
     generalFiles: {
       urlType: "bar",
@@ -51,12 +54,13 @@ export const allAnalysisType = [
     id: 104,
     parentId: 2,
     typeCode: "wind_direction_frequency",
+    typeDocxName: "wind_direction_frequency",
     typeFlag: "minute",
     generalFiles: {
-      urlType: "radarChart",
+      urlType: "",
     },
     diagramRelations: {
-      urlType: "radarChart",
+      urlType: "WindRoseChart",
     },
   },
   {
@@ -64,6 +68,7 @@ export const allAnalysisType = [
     id: 105,
     parentId: 2,
     typeCode: "wind_speed",
+    typeDocxName: "wind_speed",
     typeFlag: "minute",
     generalFiles: {
       urlType: "bar",
@@ -78,11 +83,12 @@ export const allAnalysisType = [
     parentId: 3,
     typeCode: "power_curve",
     typeFlag: "minute",
+    typeDocxName: "power_curve",
     generalFiles: {
-      urlType: "",
+      urlType: "PlotlyCharts",
     },
     diagramRelations: {
-      urlType: "",
+      urlType: "PlotlyChartsFen",
     },
   },
   {
@@ -91,6 +97,7 @@ export const allAnalysisType = [
     parentId: 3,
     typeCode: "power_scatter_2D",
     typeFlag: "minute",
+    typeDocxName: "power_scatter_2D",
     generalFiles: {
       urlType: "",
     },
@@ -103,6 +110,7 @@ export const allAnalysisType = [
     id: 108,
     parentId: 3,
     typeCode: "power_scatter",
+    typeDocxName: "power_scatter",
     typeFlag: "minute",
     generalFiles: {
       urlType: "",
@@ -117,8 +125,9 @@ export const allAnalysisType = [
     parentId: 4,
     typeCode: "rated_power_windspeed",
     typeFlag: "minute",
+    typeDocxName: "rated_power_windspeed",
     generalFiles: {
-      urlType: "",
+      urlType: "boxLineCharts",
     },
     diagramRelations: {
       urlType: "",
@@ -129,9 +138,10 @@ export const allAnalysisType = [
     id: 110,
     parentId: 4,
     typeCode: "rated_windspeed",
+    typeDocxName: "rated_windspeed",
     typeFlag: "minute",
     generalFiles: {
-      urlType: "",
+      urlType: "bar",
     },
     diagramRelations: {
       urlType: "",
@@ -142,6 +152,7 @@ export const allAnalysisType = [
     id: 111,
     parentId: 5,
     typeCode: "cp",
+    typeDocxName: "cp",
     typeFlag: "second",
     generalFiles: {
       urlType: "lineAndChildLine",
@@ -155,6 +166,7 @@ export const allAnalysisType = [
     id: 112,
     parentId: 5,
     typeCode: "cp_windspeed",
+    typeDocxName: "cp_windspeed",
     typeFlag: "second",
     generalFiles: {
       urlType: "lineAndChildLine",
@@ -168,12 +180,13 @@ export const allAnalysisType = [
     id: 113,
     parentId: 5,
     typeCode: "cp_trend",
+    typeDocxName: "cp_trend",
     typeFlag: "second",
     generalFiles: {
       urlType: "",
     },
     diagramRelations: {
-      urlType: "",
+      urlType: "boxMarkersCharts",
     },
   },
   {
@@ -181,6 +194,7 @@ export const allAnalysisType = [
     id: 114,
     parentId: 5,
     typeCode: "tsr",
+    typeDocxName: "tsr",
     typeFlag: "second",
     generalFiles: {
       urlType: "lineAndChildLine",
@@ -194,6 +208,7 @@ export const allAnalysisType = [
     id: 115,
     parentId: 5,
     typeCode: "tsr_windspeed",
+    typeDocxName: "tsr_windspeed",
     typeFlag: "second",
     generalFiles: {
       urlType: "lineAndChildLine",
@@ -207,12 +222,13 @@ export const allAnalysisType = [
     id: 116,
     parentId: 5,
     typeCode: "tsr_trend",
+    typeDocxName: "tsr_trend",
     typeFlag: "second",
     generalFiles: {
       urlType: "",
     },
     diagramRelations: {
-      urlType: "",
+      urlType: "boxMarkersCharts",
     },
   },
   {
@@ -220,6 +236,7 @@ export const allAnalysisType = [
     id: 117,
     parentId: 5,
     typeCode: "tsr_cp_power",
+    typeDocxName: "tsr_cp_power",
     typeFlag: "second",
     generalFiles: {
       urlType: "lineAndChildLine",
@@ -233,6 +250,7 @@ export const allAnalysisType = [
     id: 118,
     parentId: 5,
     typeCode: "tsr_cp_power_scatter",
+    typeDocxName: "tsr_cp_power_scatter",
     typeFlag: "second",
     generalFiles: {
       urlType: "TwoDMarkersChart",
@@ -246,6 +264,7 @@ export const allAnalysisType = [
     id: 127,
     parentId: 5,
     typeCode: "pitch_tsr_cp",
+    typeDocxName: "pitch_tsr_cp",
     typeFlag: "second",
     generalFiles: {
       urlType: "",
@@ -259,12 +278,13 @@ export const allAnalysisType = [
     id: 121,
     parentId: 6,
     typeCode: "yaw_error",
+    typeDocxName: "yaw_error",
     typeFlag: "second",
     generalFiles: {
-      urlType: "",
+      urlType: ["yawErrorBarSumChart", "yawErrorChart"],
     },
     diagramRelations: {
-      urlType: "",
+      urlType: "yawErrorLine",
     },
   },
   {
@@ -272,6 +292,7 @@ export const allAnalysisType = [
     id: 129,
     parentId: 6,
     typeCode: "yaw_error_density",
+    typeDocxName: "yaw_error_density",
     typeFlag: "second",
     generalFiles: {
       urlType: "",
@@ -286,6 +307,7 @@ export const allAnalysisType = [
     id: 122,
     parentId: 7,
     typeCode: "min_pitch",
+    typeDocxName: "min_pitch",
     typeFlag: "second",
     generalFiles: {
       urlType: "",
@@ -299,13 +321,14 @@ export const allAnalysisType = [
     id: 119,
     parentId: 8,
     typeCode: "speed_power",
+    typeDocxName: "speed_power",
     typeFlag: "minute",
     generalFiles: {
       urlType: "3DDrawingChart",
     },
     diagramRelations: {
-      FileTypeFromUrl: "speed_power", //powerMarkers2DCharts
-      urlType: ["powerMarkers2DCharts", "Time3DChart"],
+      FileTypeFromUrl: "3D_", //powerMarkers2DCharts
+      urlType: ["Time3DChart", "powerMarkers2DCharts"],
     },
   },
   {
@@ -313,13 +336,14 @@ export const allAnalysisType = [
     id: 120,
     parentId: 8,
     typeCode: "speed_torque",
+    typeDocxName: "speed_torque",
     typeFlag: "minute",
     generalFiles: {
       urlType: "3DDrawingChart",
     },
     diagramRelations: {
-      FileTypeFromUrl: "speed_torque", //powerMarkers2DCharts
-      urlType: ["powerMarkers2DCharts", "Time3DChart"],
+      FileTypeFromUrl: "3D_", //powerMarkers2DCharts
+      urlType: ["Time3DChart", "powerMarkers2DCharts"],
     },
   },
   {
@@ -327,13 +351,14 @@ export const allAnalysisType = [
     id: 123,
     parentId: 8,
     typeCode: "pitch_power",
+    typeDocxName: "pitch_power",
     typeFlag: "minute",
     generalFiles: {
       urlType: "",
     },
     diagramRelations: {
-      FileTypeFromUrl: "pitch_power", //powerMarkers2DCharts
-      urlType: ["powerMarkers2DCharts", "Time3DChart"],
+      FileTypeFromUrl: "_3D", //powerMarkers2DCharts
+      urlType: ["Time3DChart", "powerMarkers2DCharts"],
     },
   },
   {
@@ -341,6 +366,7 @@ export const allAnalysisType = [
     id: 124,
     parentId: 8,
     typeCode: "pitch_generator_speed",
+    typeDocxName: "pitch_generator_speed",
     typeFlag: "minute",
     generalFiles: {
       urlType: "",
@@ -354,6 +380,7 @@ export const allAnalysisType = [
     id: 125,
     parentId: 9,
     typeCode: "temperature_environment",
+    typeDocxName: "temperature_environment",
     typeFlag: "minute",
     generalFiles: {
       urlType: "bar",
@@ -367,6 +394,7 @@ export const allAnalysisType = [
     id: 131,
     parentId: 9,
     typeCode: "temperature_large_components",
+    typeDocxName: "temperature_large_components_hig",
     typeFlag: "minute",
     filterFileAddr: "gearbox_high_speed_shaft_bearing_temperature",
     generalFiles: {
@@ -381,6 +409,7 @@ export const allAnalysisType = [
     id: 132,
     parentId: 9,
     typeCode: "temperature_large_components",
+    typeDocxName: "temperature_large_components_mid",
     typeFlag: "minute",
     filterFileAddr: "gearboxmedium_speed_shaftbearing_temperature",
     generalFiles: {
@@ -395,6 +424,7 @@ export const allAnalysisType = [
     id: 133,
     parentId: 9,
     typeCode: "temperature_large_components",
+    typeDocxName: "temperature_large_components_low",
     typeFlag: "minute",
     filterFileAddr: "gearbox_low_speed_shaft_bearing_temperature",
     generalFiles: {
@@ -409,8 +439,10 @@ export const allAnalysisType = [
     id: 134,
     parentId: 9,
     typeCode: "temperature_large_components",
+    typeDocxName: "temperature_large_components_min",
     typeFlag: "minute",
     filterFileAddr: "main_bearing_temperature",
+
     generalFiles: {
       urlType: "lineAndChildLine",
     },
@@ -423,6 +455,7 @@ export const allAnalysisType = [
     id: 135,
     parentId: 9,
     typeCode: "temperature_large_components",
+    typeDocxName: "temperature_large_components_adriven",
     typeFlag: "minute",
     filterFileAddr: "generatordrive_end_bearing_temperature",
     generalFiles: {
@@ -437,6 +470,7 @@ export const allAnalysisType = [
     id: 136,
     parentId: 9,
     typeCode: "temperature_large_components",
+    typeDocxName: "temperature_large_components_undriven",
     typeFlag: "minute",
     filterFileAddr: "generatornon_drive_end_bearing_temperature",
     generalFiles: {
@@ -451,6 +485,7 @@ export const allAnalysisType = [
     id: 137,
     parentId: 9,
     typeCode: "temperature_large_components",
+    typeDocxName: "temperature_large_components_tem_deviation",
     typeFlag: "minute",
     filterFileAddr: "GeneratorTemperature",
     generalFiles: {
@@ -465,6 +500,7 @@ export const allAnalysisType = [
     id: 138,
     parentId: 9,
     typeCode: "temperature_large_components",
+    typeDocxName: "temperature_large_components_Winding_tem",
     typeFlag: "minute",
     filterFileAddr: "generator_winding1_temperature",
     generalFiles: {
@@ -479,12 +515,14 @@ export const allAnalysisType = [
     id: 41,
     parentId: 10,
     typeCode: "production_indicator",
+    typeDocxName: "production_indicator_unit",
     typeFlag: "minute",
     generalFiles: {
-      urlType: "",
+      FileTypeFromUrl: "production_indicator_total",
+      urlType: "productionIndicatorTotal",
     },
     diagramRelations: {
-      urlType: "radar",
+      urlType: "",
     },
   },
   {
@@ -493,8 +531,10 @@ export const allAnalysisType = [
     parentId: 10,
     typeCode: "production_indicator",
     typeFlag: "minute",
+    typeDocxName: "production_indicator_all",
     generalFiles: {
-      urlType: "",
+      FileTypeFromUrl: "production_indicator_total",
+      urlType: "productionIndicatorTotal",
     },
     diagramRelations: {
       urlType: "",
@@ -506,8 +546,10 @@ export const allAnalysisType = [
     parentId: 11,
     typeCode: "fault",
     typeFlag: "fault",
+    typeDocxName: "fault_unit",
     generalFiles: {
-      urlType: "",
+      FileTypeFromUrl: "turbine_fault_result",
+      urlType: "faultUnitChart",
     },
     diagramRelations: {
       urlType: "",
@@ -518,9 +560,11 @@ export const allAnalysisType = [
     id: 130,
     parentId: 11,
     typeCode: "fault",
+    typeDocxName: "fault_all",
     typeFlag: "fault",
     generalFiles: {
-      urlType: "",
+      FileTypeFromUrl: "total_fault_result",
+      urlType: "faultAllChart",
     },
     diagramRelations: {
       urlType: "",

+ 3 - 3
vue.config.js

@@ -66,8 +66,8 @@ module.exports = {
         // target: "http://192.168.5.4:16200", // 石月
         // target: "http://192.168.50.235:16200", // 内网
         // target: "http://192.168.5.15:16200",
-        // target: "http://192.168.50.235:16500", //演示环境
-        target: "http://106.120.102.238:26500", //外网演示环境
+        target: "http://192.168.50.235:16500", //演示环境
+        // target: "http://106.120.102.238:26500", //外网演示环境
         // target: "http://106.120.102.238:16700", // 外网16700  生产16600
         // target: "http://10.96.137.5",
         changeOrigin: true,
@@ -144,7 +144,7 @@ module.exports = {
       //nodejs 数据库数据
       "/downLoadChart": {
         target: "http://127.0.0.1:3000",
-        // target: "http://106.120.102.238:58880",//这个代理会走两次代理转发
+        // target: "http://106.120.102.238:58880",
         changeOrigin: true,
         pathRewrite: {
           "^/downLoadChart": "", // 去掉 /downLoadChart 前缀

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است