lineAndChildLine.js 5.8 KB

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