|
@@ -0,0 +1,218 @@
|
|
|
+/*
|
|
|
+ * @Author: your name
|
|
|
+ * @Date: 2025-04-14 16:09:13
|
|
|
+ * @LastEditTime: 2025-04-15 09:40:22
|
|
|
+ * @LastEditors: bogon
|
|
|
+ * @Description: In User Settings Edit
|
|
|
+ * @FilePath: /downLoadServer/src/server/utils/chartsCom/3DDrawingChart.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 generate3DDrawingChart = async (data) => {
|
|
|
+ try {
|
|
|
+ console.log("开始生成热力图...");
|
|
|
+ console.log("数据:", data);
|
|
|
+ const colorSchemesItem = colorSchemes[0].colors;
|
|
|
+ // 创建临时目录
|
|
|
+ const tempDir = path.join(process.cwd(), "images");
|
|
|
+ await fs.ensureDir(tempDir);
|
|
|
+ const tempFilePath = path.join(
|
|
|
+ tempDir,
|
|
|
+ `temp_heatmap_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]; // 修改为 data.chartData
|
|
|
+
|
|
|
+ const uniqueColors = [...new Set(chartDataset.color)];
|
|
|
+ const traces = uniqueColors.map((color, idx) => {
|
|
|
+ const colorData = chartDataset.color.map((c) => (c === color ? 1 : 0));
|
|
|
+
|
|
|
+ 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),
|
|
|
+ mode: "markers", // 根据需要设置模式
|
|
|
+ type: "scatter3d",
|
|
|
+ marker: {
|
|
|
+ size: 1, // 使用动态点大小
|
|
|
+ color: colorSchemesItem[idx], // 使用配色方案
|
|
|
+ colorscale: "YlGnBu",
|
|
|
+ },
|
|
|
+ name: ` ${color}`,
|
|
|
+ // hovertemplate: `${data.xaixs}: %{x} <br> ${data.yaixs}: %{y} <br> ${data.zaixs}: %{z} <extra></extra>`,
|
|
|
+ };
|
|
|
+ });
|
|
|
+ // 准备布局配置
|
|
|
+ const layout = {
|
|
|
+ title: {
|
|
|
+ text: chartDataset.title,
|
|
|
+ font: {
|
|
|
+ size: 16,
|
|
|
+ weight: "bold",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ scene: {
|
|
|
+ xaxis: {
|
|
|
+ gridcolor: "rgb(255,255,255)",
|
|
|
+ tickcolor: "rgb(255,255,255)",
|
|
|
+ backgroundcolor: "#CFD4DC",
|
|
|
+ showbackground: true,
|
|
|
+ linecolor: "black", // 轴线颜色
|
|
|
+ ticks: "outside", // 设置刻度线在轴线外
|
|
|
+ fixedrange: true, // 防止缩放
|
|
|
+ tickcolor: "black",
|
|
|
+ tickangle: -10,
|
|
|
+ title: {
|
|
|
+ text: data.xaixs,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ yaxis: {
|
|
|
+ type: "category",
|
|
|
+ categoryorder: "array", // 自定义顺序,确保间隔均匀
|
|
|
+ categoryarray: [...new Set(chartDataset.yData)],
|
|
|
+ gridcolor: "rgb(255,255,255)",
|
|
|
+ linecolor: "black",
|
|
|
+ ticks: "outside",
|
|
|
+ gridcolor: "rgb(255,255,255)",
|
|
|
+ tickcolor: "rgb(255,255,255)",
|
|
|
+ backgroundcolor: "#CFD4DC",
|
|
|
+ showbackground: true,
|
|
|
+ tickcolor: "black",
|
|
|
+ tickangle: 25,
|
|
|
+ title: {
|
|
|
+ text: data.yaixs,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ zaxis: {
|
|
|
+ gridcolor: "rgb(255,255,255)",
|
|
|
+ tickcolor: "rgb(255,255,255)",
|
|
|
+ backgroundcolor: "#CFD4DC",
|
|
|
+ showbackground: true,
|
|
|
+ fixedrange: true, // 防止缩放
|
|
|
+ linecolor: "black", // 轴线颜色
|
|
|
+ ticks: "outside", // 设置刻度线在轴线外
|
|
|
+ tickcolor: "black",
|
|
|
+ tickangle: -90,
|
|
|
+ title: {
|
|
|
+ text: data.zaixs,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ aspectratio: {
|
|
|
+ x: 2.2,
|
|
|
+ y: 1.7,
|
|
|
+ z: 1,
|
|
|
+ },
|
|
|
+ plot_bgcolor: "#e5ecf6",
|
|
|
+ gridcolor: "#fff",
|
|
|
+ bgcolor: "#e5ecf6", // 设置背景颜色
|
|
|
+ camera: {
|
|
|
+ up: {
|
|
|
+ x: 0.200292643688136,
|
|
|
+ y: 0.2488259353493132,
|
|
|
+ z: 0.947612004346693,
|
|
|
+ },
|
|
|
+ center: {
|
|
|
+ x: -0.052807476121180814,
|
|
|
+ y: 0.02451796399554085,
|
|
|
+ z: -0.022911006648570736,
|
|
|
+ },
|
|
|
+ eye: {
|
|
|
+ x: -2.126379643342493,
|
|
|
+ y: -2.551422475965373,
|
|
|
+ z: 1.0917667684145647,
|
|
|
+ },
|
|
|
+ projection: {
|
|
|
+ type: "orthographic",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ margin: { t: 50, b: 50, l: 50, r: 50 },
|
|
|
+ staticPlot: false,
|
|
|
+ showlegend: true,
|
|
|
+ legend: {
|
|
|
+ marker: {
|
|
|
+ size: 10, // 图例中点的大小
|
|
|
+ },
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ // 准备 HTML 内容
|
|
|
+ const htmlContent = `
|
|
|
+<!DOCTYPE html>
|
|
|
+<html>
|
|
|
+<head>
|
|
|
+ <meta charset="UTF-8">
|
|
|
+ <title>热力图</title>
|
|
|
+ <script>${plotlyContent}</script>
|
|
|
+</head>
|
|
|
+<body>
|
|
|
+ <div id="chart" style="width: 100%; height: 450px"></div>
|
|
|
+ <script>
|
|
|
+ const traces = ${JSON.stringify(traces)};
|
|
|
+ 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>
|
|
|
+`;
|
|
|
+ // ... 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: "png",
|
|
|
+ });
|
|
|
+
|
|
|
+ // 上传图片到服务器
|
|
|
+ const formData = new FormData();
|
|
|
+ formData.append("file", fs.createReadStream(tempFilePath));
|
|
|
+ return formData;
|
|
|
+ } catch (error) {
|
|
|
+ console.error("生成3D图失败:", error);
|
|
|
+ throw error;
|
|
|
+ } finally {
|
|
|
+ await browser.close();
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error("生成3D图失败:", error);
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+};
|