lineAndChildLine.js 5.8 KB

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