/* * @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} - 返回图片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 = `
`; 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(); } } };