Pārlūkot izejas kodu

处理图表渲染以及解析本地csv生成表格,表格下载功能等

liujiejie 1 mēnesi atpakaļ
vecāks
revīzija
c8f6bf157b

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


+ 43 - 1
downLoadServer/src/server/controllers/chartController.js

@@ -4,6 +4,11 @@ import { generateLineChart } from "../utils/chartsCom/lineChartsFen.js";
 import { generateLineAndChildLine } from "../utils/chartsCom/lineAndChildLine.js";
 import { generateHeatmapChart } from "../utils/chartsCom/HeatmapCharts.js";
 import { generate3DDrawingChart } from "../utils/chartsCom/3DDrawingChart.js";
+import { generateTime3DChart } from "../utils/chartsCom/Time3DChart.js";
+import { generateTwoDMarkersChart } from "../utils/chartsCom/TwoDMarkersChart.js";
+import { generateTwoDMarkersChart1 } from "../utils/chartsCom/TwoDMarkersChart1.js";
+import { generateColorbarInitTwoDmarkersChart } from "../utils/chartsCom/ColorbarInitTwoDmarkersChart.js";
+import { generatepowerMarkers2DCharts } from "../utils/chartsCom/powerMarkers2DCharts.js";
 import axios from "axios";
 
 // 提取公共逻辑到辅助函数
@@ -22,7 +27,7 @@ const handleChartGeneration = async (
     // 从URL获取数据
     const response = await axios.get(fileAddr);
     let data = [];
-    console.log("获取到的数据:", data);
+    // console.log("获取到的数据:", data);/
     if (typeof response.data === "string") {
       let dataString = response.data;
       dataString = dataString.trim(); // 去除前后空格
@@ -105,3 +110,40 @@ export const createHeatmapCharts = async (req, res) => {
 export const create3DDrawingChart = async (req, res) => {
   await handleChartGeneration(req, res, generate3DDrawingChart, "3D图生成成功");
 };
+export const createTime3DChart = async (req, res) => {
+  await handleChartGeneration(req, res, generateTime3DChart, "3D 分图生成成功");
+};
+export const createTwoDMarkersChart = async (req, res) => {
+  await handleChartGeneration(
+    req,
+    res,
+    generateTwoDMarkersChart,
+    "2D 分图生成成功"
+  );
+};
+export const createTwoDMarkersChart1 = async (req, res) => {
+  await handleChartGeneration(
+    req,
+    res,
+    generateTwoDMarkersChart1,
+    "2D 分图生成成功"
+  );
+};
+//
+export const createColorbarInitTwoDmarkersChart = async (req, res) => {
+  await handleChartGeneration(
+    req,
+    res,
+    generateColorbarInitTwoDmarkersChart,
+    "2D 分图生成成功"
+  );
+};
+export const createpowerMarkers2DCharts = async (req, res) => {
+  await handleChartGeneration(
+    req,
+    res,
+    generatepowerMarkers2DCharts,
+    "2D 分图生成成功"
+  );
+};
+//

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

@@ -5,6 +5,11 @@ import {
   createLineAndChildLine,
   createHeatmapCharts,
   create3DDrawingChart,
+  createTime3DChart,
+  createTwoDMarkersChart,
+  createTwoDMarkersChart1,
+  createColorbarInitTwoDmarkersChart,
+  createpowerMarkers2DCharts,
 } from "../controllers/chartController.js";
 
 const router = express.Router();
@@ -14,4 +19,12 @@ router.post("/line", createLineChart);
 router.post("/lineAndChildLine", createLineAndChildLine);
 router.post("/heatmap", createHeatmapCharts);
 router.post("/3DDrawingChart", create3DDrawingChart);
+router.post("/Time3DChart", createTime3DChart);
+router.post("/TwoDMarkersChart", createTwoDMarkersChart);
+router.post("/TwoDMarkersChart1", createTwoDMarkersChart1);
+router.post(
+  "/ColorbarInitTwoDmarkersChart",
+  createColorbarInitTwoDmarkersChart
+);
+router.post("/powerMarkers2DCharts", createpowerMarkers2DCharts);
 export default router;

+ 212 - 0
downLoadServer/src/server/utils/chartsCom/ColorbarInitTwoDmarkersChart.js

@@ -0,0 +1,212 @@
+/*
+ * @Author: your name
+ * @Date: 2025-04-28 10:27:00
+ * @LastEditTime: 2025-04-28 14:12:58
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /downLoadServer/src/server/utils/chartsCom/ColorbarInitTwoDmarkersChart.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 { text } from "stream/consumers";
+
+export const generateColorbarInitTwoDmarkersChart = async (data) => {
+  try {
+    const colorsBar = colorSchemes[4].colors;
+    // 创建临时目录
+    const tempDir = path.join(process.cwd(), "images");
+    await fs.ensureDir(tempDir);
+    const tempFilePath = path.join(
+      tempDir,
+      `temp_scatter_chart_${Date.now()}.png`
+    );
+
+    // 获取 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 browser = await puppeteer.launch({
+      headless: "new",
+      args: ["--no-sandbox", "--disable-setuid-sandbox"],
+    });
+
+    try {
+      const page = await browser.newPage();
+
+      // 提取散点数据
+      const scatterData = data.data && data.data[0];
+
+      // 如果有 colorbar 数据
+      const uniqueTimeLabels =
+        scatterData.colorbar &&
+        scatterData.colorbar.length === scatterData.xData.length
+          ? [...new Set(scatterData.colorbar)] // 从 colorbar 中提取唯一的标签
+          : [...new Set(scatterData.yData)]; // 如果没有 colorbar,使用 data.color
+
+      const ticktext = uniqueTimeLabels.map((dateStr) => dateStr); // 格式化为标签
+      const tickvals = uniqueTimeLabels.map((label, index) => index + 1); // 设置 tick 值
+      const timeMapping = uniqueTimeLabels.reduce((acc, curr, index) => {
+        acc[curr] = index + 1;
+        return acc;
+      }, {});
+      // 获取 colorbar 的最小值和最大值来计算比例值
+      const minValue = Math.min(...new Set(uniqueTimeLabels));
+      const maxValue = Math.max(...new Set(uniqueTimeLabels));
+      const colorStops = [
+        colorsBar[0],
+        colorsBar[4],
+        colorsBar[8],
+        colorsBar[12],
+      ];
+
+      // 计算渐变比例
+      const colors = colorStops.map((color, index) => {
+        const proportion = index / (colorStops.length - 1); // 计算比例值 (0, 1/3, 2/3, 1)
+        return [proportion, color]; // 创建比例-颜色映射
+      });
+      console.log(colors, "计算渐变比例");
+      // 计算颜色值映射
+      let colorValues = [];
+      if (
+        scatterData.colorbar &&
+        scatterData.colorbar.length === scatterData.xData.length
+      ) {
+        colorValues = scatterData.colorbar.map((date) => timeMapping[date]);
+      } else {
+        colorValues = scatterData.yData.map((date) => timeMapping[date]);
+      }
+
+      // 绘制 2D 散点图
+      const trace = {
+        x: scatterData.xData,
+        y: scatterData.yData,
+        mode: "markers",
+        type: "scattergl", // 使用 scattergl 提高性能
+        text: scatterData.engineName, // 提示文本
+        marker: {
+          color: scatterData.colorbar, // 根据 colorbar 映射的数字设置颜色
+          colorscale: colors, // 使用自定义颜色比例
+          colorbar: scatterData.colorbar
+            ? { title: { text: scatterData.colorbartitle || "" } }
+            : undefined, // 如果有 colorbar 显示,否则不显示
+          size: 4, // 点的大小
+          line: {
+            color: "#fff", // 让边框颜色和点颜色相同
+            width: 0.3, // 设置边框宽度
+          },
+        },
+      };
+
+      // 设置 hovertemplate
+      if (scatterData.colorbar) {
+        trace.customdata = scatterData.colorbar || scatterData.color;
+      }
+      if (scatterData.colorbartitle === "密度") {
+        trace.marker.cmin = 0;
+        trace.marker.cmax = maxValue;
+      } else if (scatterData.colorbartitle === "百分比(%)") {
+        trace.marker.cmin = 0;
+        trace.marker.cmax = 100;
+      }
+      // 图表布局
+      const layout = {
+        title: {
+          text: scatterData.title,
+          font: {
+            size: 16,
+            weight: "bold",
+          },
+        },
+        xaxis: {
+          title: {
+            text: data.xaixs,
+          },
+          gridcolor: "rgb(255,255,255)", // 网格线颜色
+          tickcolor: "rgb(255,255,255)",
+          backgroundcolor: "#e5ecf6",
+          showbackground: true, // 显示背景
+        },
+        yaxis: {
+          title: {
+            text: data.yaixs,
+          },
+          gridcolor: "rgb(255,255,255)", // 网格线颜色
+          tickcolor: "rgb(255,255,255)",
+          backgroundcolor: "#e5ecf6",
+          showbackground: true, // 显示背景
+        },
+        showlegend: false,
+        plot_bgcolor: "#e5ecf6",
+        gridcolor: "#fff", // 设置网格线颜色
+      };
+      if (data.xaixs === "时间") {
+        layout.xaxis.type = "date";
+        layout.xaxis.tickformat = "%Y-%m-%d";
+      }
+      // 准备 HTML 内容
+      const htmlContent = `
+        <!DOCTYPE html>
+        <html>
+        <head>
+          <meta charset="UTF-8">
+          <title>2D 散点图</title>
+          <script>${plotlyContent}</script>
+        </head>
+        <body>
+          <div id="chart" style="width: 100%; height: 600px"></div>
+          <script>
+            const traces = [${JSON.stringify(trace)}];
+            const layout = ${JSON.stringify(layout)};
+            Plotly.newPlot('chart', traces, layout, { responsive: true }).then(() => {
+              window.chartRendered = true; // 确保在图表渲染完成后设置
+              console.log("图表渲染完成");
+            }).catch((error) => {
+              console.error("图表渲染错误:", error); // 捕获渲染错误
+            });
+          </script>
+        </body>
+        </html>
+      `;
+
+      // 设置页面内容
+      await page.setContent(htmlContent, {
+        waitUntil: "networkidle0",
+      });
+
+      // 等待图表渲染完成,延长超时时间
+      await page.waitForFunction(() => window.chartRendered === true, {
+        timeout: 150000, // 延长到 150 秒
+      });
+
+      // 截图并保存到临时文件
+      const chartElement = await page.$("#chart");
+      await chartElement.screenshot({
+        path: tempFilePath,
+        type: "png",
+      });
+
+      // 上传图片到服务器
+      const formData = new FormData();
+      formData.append("file", fs.createReadStream(tempFilePath));
+      return formData;
+    } catch (error) {
+      console.error("生成2D散点图失败:", error);
+      throw error;
+    } finally {
+      await browser.close();
+    }
+  } catch (error) {
+    console.error("生成2D散点图失败:", error);
+    throw error;
+  }
+};

+ 55 - 36
downLoadServer/src/server/utils/chartsCom/Time3DChart.js

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2025-04-14 17:49:33
- * @LastEditTime: 2025-04-17 14:41:32
+ * @LastEditTime: 2025-04-25 17:02:44
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/downLoadServer/src/server/utils/chartsCom/Time3DChart.js
@@ -11,9 +11,15 @@ import fs from "fs-extra";
 import path from "path";
 import FormData from "form-data";
 import { colorSchemes } from "../colors.js";
-export const generate3DDrawingChart = async (data) => {
+// 格式化日期为 YY-MM 格式
+const formatDate = (dateString) => {
+  const date = new Date(dateString);
+  const year = date.getFullYear(); // 获取年份后两位
+  const month = ("0" + (date.getMonth() + 1)).slice(-2); // 获取月份并确保两位数
+  return `${year}-${month}`;
+};
+export const generateTime3DChart = async (data) => {
   try {
-    const colorSchemesItem = colorSchemes[0].colors;
     // 创建临时目录
     const tempDir = path.join(process.cwd(), "images");
     await fs.ensureDir(tempDir);
@@ -34,35 +40,44 @@ export const generate3DDrawingChart = async (data) => {
     // 创建浏览器实例
     const browser = await puppeteer.launch({
       headless: "new",
-      args: ["--no-sandbox", "--disable-setuid-sandbox"],
+      args: [
+        "--no-sandbox",
+        "--disable-setuid-sandbox",
+        "--window-size=1920,1080",
+      ],
     });
 
     try {
       const page = await browser.newPage();
       // 准备图表数据
-      const chartDataset = data.data[0]; // 修改为 data.chartData
-      const uniqueColors = [...new Set(chartDataset.color)];
-      const traces = uniqueColors.map((color, idx) => {
-        const colorData = chartDataset.color.map((c) => (c === color ? 1 : 0));
+      const uniqueMonths = Array.from(
+        new Set(data.data[0].yData.map((date) => formatDate(date)))
+      );
+      // 设置每个月份对应的颜色
+      const monthColors = colorSchemes[0].colors;
+      const traces = uniqueMonths.map((month, monthIndex) => {
+        const monthData = data.data[0].yData
+          .map((date, index) => (formatDate(date) === month ? index : -1))
+          .filter((index) => index !== -1);
+
         return {
-          x: chartDataset.xData.filter((_, i) => colorData[i] === 1),
-          y: chartDataset.yData.filter((_, i) => colorData[i] === 1),
-          z: chartDataset.zData.filter((_, i) => colorData[i] === 1),
+          x: monthData.map((index) => data.data[0].xData[index]), // 发电机转速
+          y: monthData.map((index) => data.data[0].yData[index]), // 时间
+          z: monthData.map((index) => data.data[0].zData[index]), // 有功功率
           mode: "markers", // 根据需要设置模式
           type: "scatter3d",
           marker: {
             size: 1, // 使用动态点大小
-            color: colorSchemesItem[idx], // 使用配色方案
+            color: monthColors[monthIndex], // 使用配色方案
             colorscale: "YlGnBu",
           },
-          name: ` ${color}`,
-          // hovertemplate: `${data.xaixs}: %{x} <br> ${data.yaixs}: %{y} <br> ${data.zaixs}: %{z} <extra></extra>`,
+          name: ` ${month}`,
         };
       });
       // 准备布局配置
       const layout = {
         title: {
-          text: chartDataset.title,
+          text: data.data[0].title,
           font: {
             size: 16,
             weight: "bold",
@@ -70,45 +85,48 @@ export const generate3DDrawingChart = async (data) => {
         },
         scene: {
           xaxis: {
-            gridcolor: "rgb(255,255,255)",
-            tickcolor: "rgb(255,255,255)",
-            backgroundcolor: "#CFD4DC",
+            gridcolor: "#fff",
+            backgroundcolor: "#e0e7f1",
             showbackground: true,
-            linecolor: "black", // 轴线颜色
-            ticks: "outside", // 设置刻度线在轴线外
-            fixedrange: true, // 防止缩放
+            linecolor: "black",
+            ticks: "outside",
+            ticklen: 10,
             tickcolor: "black",
+            zeroline: false,
             tickangle: -10,
             title: {
               text: data.xaixs,
+              standoff: 100,
             },
           },
           yaxis: {
-            type: "category",
-            categoryorder: "array", // 自定义顺序,确保间隔均匀
-            categoryarray: [...new Set(chartDataset.yData)],
-            gridcolor: "rgb(255,255,255)",
+            type: "category", // 让 Y 轴按类别均匀分布
+            categoryorder: "category ascending", // 按类别字母顺序排列
+            type: "date",
+            tickformat: "%Y-%m",
+            dtick: "M3",
+            gridcolor: "#fff",
+            tickcolor: "#e5ecf6",
+            backgroundcolor: "#e0e7f1",
+            showbackground: true,
             linecolor: "black",
             ticks: "outside",
-            gridcolor: "rgb(255,255,255)",
-            tickcolor: "rgb(255,255,255)",
-            backgroundcolor: "#CFD4DC",
-            showbackground: true,
             tickcolor: "black",
+            zeroline: false,
             tickangle: 25,
             title: {
               text: data.yaixs,
             },
           },
           zaxis: {
-            gridcolor: "rgb(255,255,255)",
-            tickcolor: "rgb(255,255,255)",
-            backgroundcolor: "#CFD4DC",
+            gridcolor: "#fff",
+            tickcolor: "#fff",
+            backgroundcolor: "#e0e7f1",
             showbackground: true,
-            fixedrange: true, // 防止缩放
-            linecolor: "black", // 轴线颜色
-            ticks: "outside", // 设置刻度线在轴线外
+            linecolor: "black",
+            ticks: "outside",
             tickcolor: "black",
+            zeroline: false,
             tickangle: -90,
             title: {
               text: data.zaixs,
@@ -122,6 +140,7 @@ export const generate3DDrawingChart = async (data) => {
           plot_bgcolor: "#e5ecf6",
           gridcolor: "#fff",
           bgcolor: "#e5ecf6", // 设置背景颜色
+          aspectmode: "manual",
           camera: {
             up: {
               x: 0.200292643688136,
@@ -163,7 +182,7 @@ export const generate3DDrawingChart = async (data) => {
           <script>${plotlyContent}</script>
         </head>
         <body>
-          <div id="chart" style="width: 100%; height: 450px"></div>
+          <div id="chart" style="width: 100%; height: 600px"></div>
           <script>
             const traces = ${JSON.stringify(traces)};
             const layout = ${JSON.stringify(layout)};

+ 178 - 0
downLoadServer/src/server/utils/chartsCom/TwoDMarkersChart.js

@@ -0,0 +1,178 @@
+/*
+ * @Author: your name
+ * @Date: 2025-04-25 16:19:33
+ * @LastEditTime: 2025-04-27 15:03:10
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /downLoadServer/src/server/utils/chartsCom/TwoDMarkersChart.js
+ */
+
+import puppeteer from "puppeteer";
+import fs from "fs-extra";
+import path from "path";
+import FormData from "form-data";
+import { colorSchemes } from "../colors.js";
+
+export const generateTwoDMarkersChart = async (data) => {
+  try {
+    // 创建临时目录
+    const tempDir = path.join(process.cwd(), "images");
+    await fs.ensureDir(tempDir);
+    const tempFilePath = path.join(
+      tempDir,
+      `temp_scatter_chart_${Date.now()}.png`
+    );
+
+    // 获取 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 browser = await puppeteer.launch({
+      headless: "new",
+      args: ["--no-sandbox", "--disable-setuid-sandbox"],
+    });
+
+    try {
+      const page = await browser.newPage();
+
+      // 准备图表数据
+      const chartDataset = data.data[0];
+      const uniqueTimeLabels =
+        chartDataset.colorbar &&
+        chartDataset.colorbar.length === chartDataset.xData.length
+          ? [...new Set(chartDataset.colorbar)]
+          : [...new Set(chartDataset.yData)];
+
+      const ticktext = uniqueTimeLabels.map((label) => label);
+      const tickvals = uniqueTimeLabels.map((_, index) => index + 1);
+      const timeMapping = uniqueTimeLabels.reduce((acc, curr, index) => {
+        acc[curr] = index + 1;
+        return acc;
+      }, {});
+
+      // 计算颜色值映射
+      let colorValues =
+        chartDataset.colorbar &&
+        chartDataset.colorbar.length === chartDataset.xData.length
+          ? chartDataset.colorbar.map((date) => timeMapping[date])
+          : chartDataset.yData.map((date) => timeMapping[date]);
+
+      // 绘制 2D 散点图
+      const trace = {
+        x: chartDataset.xData,
+        y: chartDataset.yData,
+        mode: "markers",
+        type: "scattergl", // 使用 scattergl 提高性能
+        text: chartDataset.engineName, // 提示文本
+        marker: {
+          color: colorValues,
+          colorscale: [
+            [0, "#F9FDD2"],
+            [0.15, "#E9F6BD"],
+            [0.3, "#C2E3B9"],
+            [0.45, "#8AC8BE"],
+            [0.6, "#5CA8BF"],
+            [0.75, "#407DB3"],
+            [0.9, "#2E4C9A"],
+            [1, "#1B2973"],
+          ],
+          size: new Array(chartDataset.xData.length).fill(6), // 点的大小
+        },
+      };
+
+      // 图表布局
+      const layout = {
+        title: {
+          text: chartDataset.title,
+          font: {
+            size: 16,
+            weight: "bold",
+          },
+        },
+        xaxis: {
+          title: {
+            text: data.xaixs,
+          },
+          gridcolor: "rgb(255,255,255)",
+          tickcolor: "rgb(255,255,255)",
+          backgroundcolor: "#e5ecf6",
+          showbackground: true,
+        },
+        yaxis: {
+          title: {
+            text: data.yaixs,
+          },
+          gridcolor: "rgb(255,255,255)",
+          tickcolor: "rgb(255,255,255)",
+          backgroundcolor: "#e5ecf6",
+          showbackground: true,
+        },
+        showlegend: false,
+        plot_bgcolor: "#e5ecf6",
+        gridcolor: "#fff",
+      };
+
+      // 准备 HTML 内容
+      const htmlContent = `
+        <!DOCTYPE html>
+        <html>
+        <head>
+          <meta charset="UTF-8">
+          <title>2D 散点图</title>
+          <script>${plotlyContent}</script>
+        </head>
+        <body>
+          <div id="chart" style="width: 100%; height: 600px"></div>
+          <script>
+            const traces = [${JSON.stringify(trace)}];
+            const layout = ${JSON.stringify(layout)};
+            Plotly.newPlot('chart', traces, layout, { responsive: true }).then(() => {
+              window.chartRendered = true; // 确保在图表渲染完成后设置
+              console.log("图表渲染完成");
+            }).catch((error) => {
+              console.error("图表渲染错误:", error); // 捕获渲染错误
+            });
+          </script>
+        </body>
+        </html>
+      `;
+
+      // 设置页面内容
+      await page.setContent(htmlContent, {
+        waitUntil: "networkidle0",
+      });
+
+      // 等待图表渲染完成,延长超时时间
+      await page.waitForFunction(() => window.chartRendered === true, {
+        timeout: 150000, // 延长到 150 秒
+      });
+
+      // 截图并保存到临时文件
+      const chartElement = await page.$("#chart");
+      await chartElement.screenshot({
+        path: tempFilePath,
+        type: "png",
+      });
+
+      // 上传图片到服务器
+      const formData = new FormData();
+      formData.append("file", fs.createReadStream(tempFilePath));
+      return formData;
+    } catch (error) {
+      console.error("生成2D散点图失败:", error);
+      throw error;
+    } finally {
+      await browser.close();
+    }
+  } catch (error) {
+    console.error("生成2D散点图失败:", error);
+    throw error;
+  }
+};

+ 200 - 0
downLoadServer/src/server/utils/chartsCom/TwoDMarkersChart1.js

@@ -0,0 +1,200 @@
+import puppeteer from "puppeteer";
+import fs from "fs-extra";
+import path from "path";
+import FormData from "form-data";
+import { colorSchemes } from "../colors.js";
+
+export const generateTwoDMarkersChart1 = async (data) => {
+  try {
+    // 创建临时目录
+    const tempDir = path.join(process.cwd(), "images");
+    await fs.ensureDir(tempDir);
+    const tempFilePath = path.join(
+      tempDir,
+      `temp_scatter_chart_${Date.now()}.png`
+    );
+
+    // 获取 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 browser = await puppeteer.launch({
+      headless: "new",
+      args: ["--no-sandbox", "--disable-setuid-sandbox"],
+    });
+
+    try {
+      const page = await browser.newPage();
+
+      // 提取散点数据和线数据
+      const scatterData = data.data.filter(
+        (item) => item.engineName !== "合同功率曲线"
+      )[0]; // 点数据
+
+      const lineData = data.data.filter(
+        (item) =>
+          item.engineName === "合同功率曲线" ||
+          item.enginName === "合同功率曲线"
+      )[0]; // 线数据
+
+      // 提取唯一时间标签,并计算 tickvals 和 ticktext
+      const uniqueTimeLabels = scatterData.colorbar
+        ? [...new Set(scatterData.colorbar)]
+        : [...new Set(scatterData.color)];
+      const tickvals = uniqueTimeLabels.map((_, index) => index + 1);
+      const ticktext = uniqueTimeLabels.map((dateStr) => {
+        const date = new Date(dateStr);
+        return date.toLocaleDateString("en-CA", {
+          year: "numeric",
+          month: "2-digit",
+        });
+      });
+
+      const timeMapping = uniqueTimeLabels.reduce((acc, curr, index) => {
+        acc[curr] = index + 1;
+        return acc;
+      }, {});
+
+      // 计算颜色值映射
+      let colorValues = scatterData.colorbar
+        ? scatterData.colorbar.map((date) => timeMapping[date])
+        : scatterData.color.map((date) => timeMapping[date]);
+
+      // 绘制散点图
+      const scatterTrace = {
+        x: scatterData.xData,
+        y: scatterData.yData,
+        mode: "markers",
+        type: "scattergl", // 使用 scattergl 提高性能
+        text: scatterData.engineName, // 提示文本
+        marker: {
+          color: colorValues,
+          colorscale: [
+            [0, "#F9FDD2"],
+            [0.15, "#E9F6BD"],
+            [0.3, "#C2E3B9"],
+            [0.45, "#8AC8BE"],
+            [0.6, "#5CA8BF"],
+            [0.75, "#407DB3"],
+            [0.9, "#2E4C9A"],
+            [1, "#1B2973"],
+          ],
+          size: new Array(scatterData.xData.length).fill(6), // 点的大小
+        },
+        hovertemplate: `${data.xaixs}: %{x} <br> ${data.yaixs}: %{y} <br> 时间: %{customdata}<extra></extra>`,
+        customdata: scatterData.colorbar || scatterData.color, // 将格式化后的时间存入 customdata
+      };
+
+      // 绘制线图
+      let lineTrace = {};
+      if (lineData) {
+        lineTrace = {
+          x: lineData.xData,
+          y: lineData.yData,
+          mode: "lines+markers", // 线和点同时显示
+          type: "scattergl", // 使用 scattergl 类型
+          text: lineData.engineName, // 提示文本
+          line: {
+            color: "red", // 线条颜色
+          },
+        };
+      }
+      console.log(lineData, lineTrace, "2222");
+      // 图表布局
+      const layout = {
+        title: {
+          text: scatterData.title,
+          font: {
+            size: 16,
+            weight: "bold",
+          },
+        },
+        xaxis: {
+          title: {
+            text: data.xaixs,
+          },
+          gridcolor: "rgb(255,255,255)",
+          tickcolor: "rgb(255,255,255)",
+          backgroundcolor: "#e5ecf6",
+          showbackground: true,
+        },
+        yaxis: {
+          title: {
+            text: data.yaixs,
+          },
+          gridcolor: "rgb(255,255,255)",
+          tickcolor: "rgb(255,255,255)",
+          backgroundcolor: "#e5ecf6",
+          showbackground: true,
+        },
+        showlegend: false,
+        plot_bgcolor: "#e5ecf6",
+        gridcolor: "#fff",
+      };
+
+      // 准备 HTML 内容
+      const htmlContent = `
+        <!DOCTYPE html>
+        <html>
+        <head>
+          <meta charset="UTF-8">
+          <title>2D 散点图</title>
+          <script>${plotlyContent}</script>
+        </head>
+        <body>
+          <div id="chart" style="width: 100%; height: 600px"></div>
+          <script>
+            const traces = [${JSON.stringify(scatterTrace)}${
+        lineTrace ? `, ${JSON.stringify(lineTrace)}` : ""
+      }];
+            const layout = ${JSON.stringify(layout)};
+            Plotly.newPlot('chart', traces, layout, { responsive: true }).then(() => {
+              window.chartRendered = true; // 确保在图表渲染完成后设置
+              console.log("图表渲染完成");
+            }).catch((error) => {
+              console.error("图表渲染错误:", error); // 捕获渲染错误
+            });
+          </script>
+        </body>
+        </html>
+      `;
+
+      // 设置页面内容
+      await page.setContent(htmlContent, {
+        waitUntil: "networkidle0",
+      });
+
+      // 等待图表渲染完成,延长超时时间
+      await page.waitForFunction(() => window.chartRendered === true, {
+        timeout: 150000, // 延长到 150 秒
+      });
+
+      // 截图并保存到临时文件
+      const chartElement = await page.$("#chart");
+      await chartElement.screenshot({
+        path: tempFilePath,
+        type: "png",
+      });
+
+      // 上传图片到服务器
+      const formData = new FormData();
+      formData.append("file", fs.createReadStream(tempFilePath));
+      return formData;
+    } catch (error) {
+      console.error("生成2D散点图失败:", error);
+      throw error;
+    } finally {
+      await browser.close();
+    }
+  } catch (error) {
+    console.error("生成2D散点图失败:", error);
+    throw error;
+  }
+};

+ 247 - 0
downLoadServer/src/server/utils/chartsCom/powerMarkers2DCharts.js

@@ -0,0 +1,247 @@
+/*
+ * @Author: your name
+ * @Date: 2025-04-28 14:15:23
+ * @LastEditTime: 2025-04-28 14:33:09
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /downLoadServer/src/server/utils/chartsCom/powerMarkers2DCharts.js
+ */
+import puppeteer from "puppeteer";
+import fs from "fs-extra";
+import path from "path";
+import FormData from "form-data";
+import { colorSchemes } from "../colors.js";
+
+export const generatepowerMarkers2DCharts = async (data) => {
+  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`
+    );
+
+    // 获取 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 browser = await puppeteer.launch({
+      headless: "new",
+      args: ["--no-sandbox", "--disable-setuid-sandbox"],
+    });
+
+    try {
+      const page = await browser.newPage();
+
+      // 提取散点数据和线数据
+      const scatterData = data.data.filter(
+        (item) => item.engineName !== "合同功率曲线"
+      )[0]; // 点数据
+
+      const lineData = data.data.filter(
+        (item) => item.engineName === "合同功率曲线"
+      )[0]; // 线数据
+
+      // 保存原始颜色和大小
+      const originalColors = [...scatterData.yData];
+      const originalSizes = new Array(scatterData.xData.length).fill(6); // 初始点大小
+
+      // 如果有 colorbar 数据
+      const uniqueTimeLabels =
+        scatterData.colorbar &&
+        scatterData.colorbar.length === scatterData.xData.length
+          ? [...new Set(scatterData.colorbar)] // 从 colorbar 中提取唯一的标签
+          : [...new Set(scatterData.color)]; // 如果没有 colorbar,使用 data.color
+
+      const ticktext = uniqueTimeLabels.map((dateStr) => dateStr); // 格式化为标签
+      const tickvals = uniqueTimeLabels.map((label, index) => index + 1); // 设置 tick 值
+      const timeMapping = uniqueTimeLabels.reduce((acc, curr, index) => {
+        acc[curr] = index + 1;
+        return acc;
+      }, {});
+
+      // 获取 colorbar 的最小值和最大值来计算比例值
+      const minValue = Math.min(...tickvals);
+      const maxValue = Math.max(...tickvals);
+
+      // 仅取 colorsBar 的前 4 种颜色进行渐变
+      const colorStops = [
+        colorsBar[0],
+        colorsBar[4],
+        colorsBar[8],
+        colorsBar[12],
+      ];
+
+      // 计算渐变比例
+      const colors = colorStops.map((color, index) => {
+        const proportion = index / (colorStops.length - 1); // 计算比例值 (0, 1/3, 2/3, 1)
+        return [proportion, color]; // 生成颜色映射
+      });
+
+      // 确保至少有 2 个颜色,否则使用默认颜色
+      if (colors.length < 2) {
+        colors.push([1, colorStops[colorStops.length - 1] || "#1B2973"]);
+      }
+
+      // 计算颜色值映射
+      let colorValues = [];
+      if (scatterData.colorbar) {
+        colorValues = scatterData.colorbar.map((date) => timeMapping[date]);
+      } else {
+        colorValues = scatterData.color.map((date) => timeMapping[date]);
+      }
+
+      // 绘制散点图
+      const scatterTrace = {
+        x: scatterData.xData,
+        y: scatterData.yData,
+        mode: "markers", // 散点
+        type: "scattergl", // 使用散点图
+        text: scatterData.engineName, // 提示文本
+        marker: {
+          color: colorValues, // 使用时间数据来映射颜色
+          colorscale: colors
+            ? [...colors]
+            : [
+                [0, "#F9FDD2"],
+                [0.15, "#E9F6BD"],
+                [0.3, "#C2E3B9"],
+                [0.45, "#8AC8BE"],
+                [0.6, "#5CA8BF"],
+                [0.75, "#407DB3"],
+                [0.9, "#2E4C9A"],
+                [1, "#1B2973"],
+              ], // 默认颜色渐变
+          colorbar: {
+            title: scatterData.colorbartitle || "Color Legend", // 色标标题
+            tickvals: tickvals, // 设置刻度值
+            ticktext: ticktext, // 设置刻度文本
+            tickmode: "array", // 使用数组模式
+            tickangle: -45, // 可选:调整刻度文本的角度
+          },
+          size: 4, // 点的大小
+          line: {
+            color: "#fff", // 让边框颜色和点颜色相同
+            width: 0.3, // 设置边框宽度
+          },
+        },
+        hovertemplate:
+          `${data.xaixs}: %{x} <br> ` +
+          `${data.yaixs}: %{y} <br> ` +
+          `时间: %{customdata}<extra></extra>`, // 在 hover 中显示格式化后的时间
+        customdata: scatterData.colorbar || scatterData.color, // 将格式化后的时间存入 customdata
+      };
+
+      // 绘制线图
+      let lineTrace = {};
+      if (lineData) {
+        lineTrace = {
+          x: lineData.xData, // 线数据的 xData
+          y: lineData.yData, // 线数据的 yData
+          mode: "lines+markers", // 线和点同时显示
+          type: "scattergl", // 使用 scattergl 类型
+          text: lineData.engineName, // 提示文本
+          line: {
+            color: "red", // 线条颜色
+          },
+        };
+      }
+
+      // 图表布局
+      const layout = {
+        title: {
+          text: scatterData.title,
+          font: {
+            size: 16,
+            weight: "bold",
+          },
+        },
+        xaxis: {
+          title: {
+            text: data.xaixs,
+          },
+          gridcolor: "rgb(255,255,255)", // 网格线颜色
+          tickcolor: "rgb(255,255,255)",
+          backgroundcolor: "#e5ecf6",
+          showbackground: true, // 显示背景
+        },
+        yaxis: {
+          title: { text: data.yaixs },
+          gridcolor: "rgb(255,255,255)", // 网格线颜色
+          tickcolor: "rgb(255,255,255)",
+          backgroundcolor: "#e5ecf6",
+          showbackground: true, // 显示背景
+        },
+        showlegend: false,
+        plot_bgcolor: "#e5ecf6",
+        gridcolor: "#fff", // 设置网格线颜色
+      };
+
+      // 准备 HTML 内容
+      const htmlContent = `
+        <!DOCTYPE html>
+        <html>
+        <head>
+          <meta charset="UTF-8">
+          <title>2D 散点图</title>
+          <script>${plotlyContent}</script>
+        </head>
+        <body>
+          <div id="chart" style="width: 100%; height: 600px"></div>
+          <script>
+            const traces = [${JSON.stringify(scatterTrace)}${
+        lineTrace ? `, ${JSON.stringify(lineTrace)}` : ""
+      }];
+            const layout = ${JSON.stringify(layout)};
+            Plotly.newPlot('chart', traces, layout, { responsive: true }).then(() => {
+              window.chartRendered = true; // 确保在图表渲染完成后设置
+              console.log("图表渲染完成");
+            }).catch((error) => {
+              console.error("图表渲染错误:", error); // 捕获渲染错误
+            });
+          </script>
+        </body>
+        </html>
+      `;
+
+      // 设置页面内容
+      await page.setContent(htmlContent, {
+        waitUntil: "networkidle0",
+      });
+
+      // 等待图表渲染完成,延长超时时间
+      await page.waitForFunction(() => window.chartRendered === true, {
+        timeout: 150000, // 延长到 150 秒
+      });
+
+      // 截图并保存到临时文件
+      const chartElement = await page.$("#chart");
+      await chartElement.screenshot({
+        path: tempFilePath,
+        type: "png",
+      });
+
+      // 上传图片到服务器
+      const formData = new FormData();
+      formData.append("file", fs.createReadStream(tempFilePath));
+      return formData;
+    } catch (error) {
+      console.error("生成2D散点图失败:", error);
+      throw error;
+    } finally {
+      await browser.close();
+    }
+  } catch (error) {
+    console.error("生成2D散点图失败:", error);
+    throw error;
+  }
+};

+ 8 - 5
src/views/performance/assetssMag.vue

@@ -49,7 +49,6 @@
           >
           <el-button @click="reset('ruleForm')" size="small">重置</el-button>
         </el-form-item>
-
         <el-form-item class="right-align">
           <el-button @click="Newanalyse" size="small">创建分析</el-button>
           <el-button
@@ -64,7 +63,7 @@
             v-hasPermi="['home:performance:autoAssetss']"
             >查看自动分析列表</el-button
           >
-          <!-- <el-button @click="handleDownLoadChart" size="small">下载</el-button> -->
+          <el-button @click="handleDownLoadChart" size="small">下载</el-button>
         </el-form-item>
       </el-form>
     </div>
@@ -535,7 +534,7 @@ export default {
   methods: {
     async handleDownLoadChart() {
       const res = await axios.post(
-        "/downLoadChart/chartServer/charts/3DDrawingChart",
+        "/downLoadChart/chartServer/charts/powerMarkers2DCharts",
         {
           fieldEngineCode: "WOG00623", //lineChartFen
           fileAddr:
@@ -544,8 +543,12 @@ export default {
             // "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_power/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
         }
       );
       console.log(res, "res linechart");

+ 3 - 1
src/views/performance/components/chartsCom/ColorbarInitTwoDmarkersChart.vue

@@ -1,7 +1,7 @@
 <!--
  * @Author: your name
  * @Date: 2025-03-14 10:30:00
- * @LastEditTime: 2025-04-11 16:54:48
+ * @LastEditTime: 2025-04-28 14:08:16
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/performance/components/chartsCom/colorbarInitTwoDmarkersChart.vue
@@ -201,12 +201,14 @@ export default {
           data.colorbar && data.colorbar.length === data.xData.length
             ? [...new Set(data.colorbar)] // 从 colorbar 中提取唯一的标签
             : [...new Set(data.yData)]; // 如果没有 colorbar,使用 data.color
+
         const ticktext = uniqueTimeLabels.map((dateStr) => dateStr); // 格式化为标签
         const tickvals = uniqueTimeLabels.map((label, index) => index + 1); // 设置 tick 值
         const timeMapping = uniqueTimeLabels.reduce((acc, curr, index) => {
           acc[curr] = index + 1;
           return acc;
         }, {});
+        console.log("timeMapping", timeMapping);
         // 获取 colorbar 的最小值和最大值来计算比例值
         const minValue = Math.min(...new Set(uniqueTimeLabels));
         const maxValue = Math.max(...new Set(uniqueTimeLabels));

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

@@ -90,7 +90,6 @@ export default {
       colorSchemes: [...colorSchemes],
       chartData: {},
       chartType: "scatter", // 初始化为散点图 (scatter)
-      // color1: "", // 默认颜色
       selectedPoints: [],
       originalColors: [],
       originalSizes: [],

+ 1 - 26
src/views/performance/components/chartsCom/TwoDMarkersChart1.vue

@@ -1,7 +1,7 @@
 <!--
  * @Author: your name
  * @Date: 2024-09-11 14:32:12
- * @LastEditTime: 2025-04-11 16:55:33
+ * @LastEditTime: 2025-04-27 15:23:06
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/performance/components/chartsCom/powerMarkers2DCharts.vue
@@ -216,32 +216,7 @@ export default {
       } else {
         colorValues = data.color.map((date, index) => timeMapping[date]);
       }
-      // 仅取 this.color1 的前 4 种颜色进行渐变
-      //   const colorStops = [
-      //     this.color1[0],
-      //     this.color1[4],
-      //     this.color1[8],
-      //     this.color1[12],
-      //   ];
 
-      //   // 计算渐变比例
-      //   const colors = colorStops.map((color, index) => {
-      //     const proportion = index / (colorStops.length - 1); // 计算比例值 (0, 1/3, 2/3, 1)
-      //     return [proportion, color]; // 生成颜色映射
-      //   });
-
-      //   // 确保至少有 2 个颜色,否则使用默认颜色
-      //   if (colors.length < 2) {
-      //     colors.push([1, colorStops[colorStops.length - 1] || "#1B2973"]);
-      //   }
-
-      //   // 计算颜色值映射
-      //   let colorValues = [];
-      //   if (data.colorbar) {
-      //     colorValues = data.colorbar.map((date) => timeMapping[date]);
-      //   } else {
-      //     colorValues = data.color.map((date) => timeMapping[date]);
-      //   }
       let scatterTrace = {}; // 用于存放散点图的 trace
       let lineTrace = {}; // 用于存放折线图的 trace
 

+ 2 - 18
src/views/performance/components/chartsCom/powerMarkers2DCharts.vue

@@ -1,7 +1,7 @@
 <!--
  * @Author: your name
  * @Date: 2024-09-11 14:32:12
- * @LastEditTime: 2025-03-21 14:57:48
+ * @LastEditTime: 2025-04-28 14:16:16
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/performance/components/chartsCom/powerMarkers2DCharts.vue
@@ -199,23 +199,7 @@ export default {
       // 获取 yData 的最小值和最大值来做比例值的计算
       const minValue = Math.min(...tickvals);
       const maxValue = Math.max(...tickvals);
-      // // 计算比例值
-      // const colors = ticktext.map((item, ind) => {
-      //   // 计算比例值(可以根据需要调整映射的数据范围)
-      //   const proportion = (tickvals[ind] - minValue) / (maxValue - minValue);
-      //   return [
-      //     proportion, // 比例值
-      //     this.color1[ind], // 对应的颜色
-      //   ];
-      // });
-      // // 将时间字符串映射为数字
-      // let colorValues = [];
-      // // data.colorbar.map((date, index) => timeMapping[date]);
-      // if (data.colorbar) {
-      //   colorValues = data.colorbar.map((date, index) => timeMapping[date]);
-      // } else {
-      //   colorValues = data.color.map((date, index) => timeMapping[date]);
-      // }
+
       // 仅取 this.color1 的前 4 种颜色进行渐变
       const colorStops = [
         this.color1[0],

+ 2 - 2
vue.config.js

@@ -77,8 +77,8 @@ module.exports = {
       },
       // 未知量  //振动、激光测距仪
       "/WZLapi": {
-        // target: "http://192.168.50.241:9001", // WZLapi 目标地址
-        target: "http://106.120.102.238:18080/WindTransDev", // WZLapi 外网目标地址
+        target: "http://192.168.50.241:9001", // WZLapi 目标地址
+        // target: "http://106.120.102.238:18080/WindTransDev", // WZLapi 外网目标地址
         changeOrigin: true,
         pathRewrite: {
           "^/WZLapi": "", // 去掉 /WZLapi 前缀