lineChartsFen.js 5.3 KB

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