lineAndChildLine.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. import puppeteer from "puppeteer";
  2. import fs from "fs-extra";
  3. import path from "path";
  4. import axios from "axios";
  5. import FormData from "form-data";
  6. import { colorSchemes } from "../colors.js";
  7. /**
  8. * 生成折线图并上传
  9. * @param {Object} options - 配置选项
  10. * @param {string} options.fileAddr - 数据文件地址
  11. * @param {string[]} options.color1 - 颜色数组1
  12. * @param {string[]} options.colors - 颜色数组2
  13. * @param {Object[]} options.setUpImgData - 图表设置数据
  14. * @returns {Promise<String>} - 返回图片URL
  15. */
  16. export const generateLineAndChildLine = async (options) => {
  17. const { fileAddr } = options;
  18. try {
  19. console.log("开始生成折线图...");
  20. // 获取数据
  21. const resultChartsData = await axios.get(fileAddr);
  22. const chartData = resultChartsData.data;
  23. // 准备图表数据
  24. const data = [];
  25. chartData.data &&
  26. chartData.data.forEach((turbine, index) => {
  27. const chartConfig = {
  28. x: turbine.xData,
  29. y: turbine.yData,
  30. name: turbine.engineName,
  31. mode: "lines",
  32. line: {
  33. color: colorSchemes[index % colorSchemes.length],
  34. },
  35. marker: {
  36. color: colorSchemes[index % colorSchemes.length],
  37. },
  38. hovertemplate:
  39. `${chartData.xaixs}:` +
  40. ` %{x} <br> ` +
  41. `${chartData.yaixs}:` +
  42. "%{y} <br>",
  43. };
  44. if (chartData.yaixs === "概率密度函数") {
  45. chartConfig.line.color = colorSchemes[0][12];
  46. }
  47. data.push(chartConfig);
  48. });
  49. // 准备布局配置
  50. const layout = {
  51. title: {
  52. text: chartData.title,
  53. font: {
  54. size: 16,
  55. weight: "bold",
  56. },
  57. },
  58. xaxis: {
  59. title: chartData.xaixs || "X轴",
  60. gridcolor: "rgb(255,255,255)",
  61. tickcolor: "rgb(255,255,255)",
  62. backgroundcolor: "#e5ecf6",
  63. },
  64. yaxis: {
  65. title: chartData.yaixs || "Y轴",
  66. gridcolor: "rgb(255,255,255)",
  67. tickcolor: "rgb(255,255,255)",
  68. backgroundcolor: "#e5ecf6",
  69. },
  70. margin: {
  71. l: 50,
  72. r: 50,
  73. t: 50,
  74. b: 50,
  75. },
  76. plot_bgcolor: "#e5ecf6",
  77. gridcolor: "#fff",
  78. bgcolor: "#e5ecf6",
  79. autosize: true,
  80. barmode: "group",
  81. };
  82. if (
  83. chartData.contract_Cp_curve_yData &&
  84. chartData.contract_Cp_curve_yData.length > 0
  85. ) {
  86. data.push({
  87. x: chartData.contract_Cp_curve_xData,
  88. y: chartData.contract_Cp_curve_yData,
  89. mode: "lines+markers",
  90. name: "合同功率曲线",
  91. line: {
  92. color: "red",
  93. width: 1,
  94. },
  95. marker: { color: "red", size: 4 },
  96. });
  97. }
  98. // 创建临时目录
  99. const tempDir = path.join(process.cwd(), "temp");
  100. await fs.ensureDir(tempDir);
  101. const tempFilePath = path.join(
  102. tempDir,
  103. `temp_line_chart_${Date.now()}.png`
  104. );
  105. // 获取 plotly.js 的绝对路径
  106. const plotlyPath = path.join(
  107. process.cwd(),
  108. "src",
  109. "public",
  110. "js",
  111. "plotly-latest.min.js"
  112. );
  113. const plotlyContent = await fs.readFile(plotlyPath, "utf-8");
  114. // 创建浏览器实例
  115. const browser = await puppeteer.launch({
  116. headless: "new",
  117. args: ["--no-sandbox", "--disable-setuid-sandbox"],
  118. });
  119. try {
  120. const page = await browser.newPage();
  121. // 创建HTML内容
  122. const htmlContent = `
  123. <!DOCTYPE html>
  124. <html>
  125. <head>
  126. <script>${plotlyContent}</script>
  127. <style>
  128. body { margin: 0; }
  129. #chart { width: 800px; height: 600px; }
  130. </style>
  131. </head>
  132. <body>
  133. <div id="chart"></div>
  134. <script>
  135. window.onload = function() {
  136. const data = ${JSON.stringify(data)};
  137. const layout = ${JSON.stringify(layout)};
  138. Plotly.newPlot('chart', data, layout, {
  139. responsive: true
  140. }).then(() => {
  141. window.chartRendered = true;
  142. });
  143. };
  144. </script>
  145. </body>
  146. </html>
  147. `;
  148. // 设置页面内容
  149. await page.setContent(htmlContent, {
  150. waitUntil: "networkidle0",
  151. });
  152. // 等待图表渲染完成
  153. await page.waitForFunction(() => window.chartRendered === true, {
  154. timeout: 60000,
  155. });
  156. // 截图并保存到临时文件
  157. const chartElement = await page.$("#chart");
  158. await chartElement.screenshot({
  159. path: tempFilePath,
  160. type: "png",
  161. });
  162. // 上传图片到服务器
  163. const formData = new FormData();
  164. formData.append("file", fs.createReadStream(tempFilePath));
  165. formData.append("type", "chart");
  166. // 这里假设需要从 chartData 中获取 engineCode 和 analysisTypeCode,根据实际情况调整
  167. formData.append("engineCode", chartData.engineCode || "");
  168. formData.append("analysisTypeCode", chartData.analysisTypeCode || "");
  169. // const uploadResponse = await axios.post(
  170. // "http://localhost:6900/upload",
  171. // formData,
  172. // {
  173. // headers: {
  174. // ...formData.getHeaders(),
  175. // },
  176. // }
  177. // );
  178. // 删除临时文件
  179. await fs.remove(tempFilePath);
  180. // console.log("折线图生成并上传成功:", uploadResponse.data.url);
  181. // return uploadResponse.data.url;
  182. return formData;
  183. } finally {
  184. await browser.close();
  185. }
  186. } catch (error) {
  187. console.error("生成折线图失败:", error);
  188. throw error;
  189. }
  190. };