/* * @Author: your name * @Date: 2025-04-14 17:49:33 * @LastEditTime: 2025-05-21 15:11:06 * @LastEditors: bogon * @Description: In User Settings Edit * @FilePath: /performance-test/downLoadServer/src/server/utils/chartsCom/Time3DChart.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"; // 格式化日期为 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, bucketName, objectName) => { 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-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", "--window-size=1920,1080", ], }); try { const page = await browser.newPage(); // 准备图表数据 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: 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: monthColors[monthIndex], // 使用配色方案 colorscale: "YlGnBu", }, name: ` ${month}`, }; }); // 准备布局配置 const layout = { title: { text: data.data[0].title, font: { size: 16, weight: "bold", }, }, scene: { xaxis: { gridcolor: "#fff", backgroundcolor: "#e0e7f1", showbackground: true, linecolor: "black", ticks: "outside", ticklen: 10, tickcolor: "black", zeroline: false, tickangle: -10, title: { text: data.xaixs, standoff: 100, }, }, yaxis: { 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", tickcolor: "black", zeroline: false, tickangle: 25, title: { text: data.yaixs, }, }, zaxis: { gridcolor: "#fff", tickcolor: "#fff", backgroundcolor: "#e0e7f1", showbackground: true, linecolor: "black", ticks: "outside", tickcolor: "black", zeroline: false, tickangle: -90, title: { text: data.zaixs, }, }, plot_bgcolor: "#e5ecf6", gridcolor: "#fff", bgcolor: "#e5ecf6", // 设置背景颜色 aspectmode: "manual", aspectratio: { x: 2.0000000000000018, y: 1.5454545454545465, z: 0.9090909090909098, }, camera: { up: { x: 0.19380723218588866, y: 0.2540224158731985, z: 0.9475818534492884, }, center: { x: -0.052807476121180814, y: 0.02451796399554085, z: -0.022911006648570736, }, eye: { x: -2.126389777109588, y: -2.5514260394238466, z: 1.091739681861482, }, projection: { type: "orthographic", }, }, }, margin: { t: 50, b: 50, l: 50, r: 50 }, staticPlot: false, showlegend: true, legend: { marker: { size: 10, // 图例中点的大小 }, }, }; // 准备 HTML 内容 const htmlContent = ` 3D图
`; // ... existing code ... // 设置页面内容 await page.setContent(htmlContent, { waitUntil: "networkidle0", }); // 等待图表渲染完成,延长超时时间 await page.waitForFunction(() => window.chartRendered === true, { timeout: 150000, // 延长到 120 秒 }); // 截图并保存到临时文件 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; } catch (error) { console.error("生成3D图失败:", error); throw error; } finally { await browser.close(); } } catch (error) { console.error("生成3D图失败:", error); throw error; } };