123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- /*
- * @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();
- }
- }
- };
|