liujiejie 2 місяців тому
батько
коміт
055ee205f9

+ 2 - 0
downLoadServer/.env

@@ -0,0 +1,2 @@
+PLOTLY_USERNAME=your-username
+PLOTLY_API_KEY=your-api-key 

+ 19 - 0
downLoadServer/index.mjs

@@ -0,0 +1,19 @@
+/*
+ * @Author: your name
+ * @Date: 2025-04-07 15:19:55
+ * @LastEditTime: 2025-04-07 15:27:15
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/downLoadServer/index.js
+ */
+//node 版本v18.12.1
+import "dotenv/config";
+import { startServer } from "./src/server/server.js";
+
+try {
+  startServer();
+  console.log("服务器启动成功!");
+} catch (error) {
+  console.error("服务器启动失败:", error);
+  process.exit(1);
+}

+ 22 - 0
downLoadServer/package.json

@@ -0,0 +1,22 @@
+{
+  "name": "download-server",
+  "version": "1.0.0",
+  "description": "",
+  "main": "index.mjs",
+  "type": "module",
+  "scripts": {
+    "start": "node index.mjs",
+    "test": "node src/test.mjs"
+  },
+  "keywords": [],
+  "author": "",
+  "license": "ISC",
+  "dependencies": {
+    "axios": "^1.6.8",
+    "dotenv": "^16.4.7",
+    "express": "^4.19.1",
+    "form-data": "^4.0.2",
+    "fs-extra": "^11.2.0",
+    "puppeteer": "^22.6.0"
+  }
+}

BIN
downLoadServer/src/images/bar_chart_1744075925393.png


Різницю між файлами не показано, бо вона завелика
+ 7 - 0
downLoadServer/src/public/js/plotly-latest.min.js


+ 4 - 0
downLoadServer/src/server/config.js

@@ -0,0 +1,4 @@
+export const serverConfig = {
+  port: 3000,
+  host: "127.0.0.1",
+};

+ 50 - 0
downLoadServer/src/server/controllers/chartController.js

@@ -0,0 +1,50 @@
+import { successResponse, errorResponse } from "../utils/response.js";
+import { generateBarChart } from "../utils/chartsCom/BarChart.js";
+import axios from "axios";
+
+export const createBarChart = async (req, res) => {
+  try {
+    const { dataUrl } = req.body;
+
+    if (!dataUrl) {
+      return errorResponse(res, "缺少数据URL", 400);
+    }
+
+    // 从URL获取数据
+    const response = await axios.get(dataUrl);
+    const data = response.data;
+
+    // 验证数据格式
+    if (
+      !data ||
+      !data.data ||
+      !data.data[0] ||
+      !data.data[0].xData ||
+      !data.data[0].yData
+    ) {
+      return errorResponse(res, "获取的数据格式不正确", 400);
+    }
+
+    // 生成图表并上传
+    const imageUrl = await generateBarChart(data);
+
+    successResponse(
+      res,
+      {
+        imageUrl: imageUrl,
+      },
+      "图表生成成功"
+    );
+  } catch (error) {
+    console.error("创建图表失败:", error);
+    if (error.response) {
+      errorResponse(
+        res,
+        `获取数据失败: ${error.response.status}`,
+        error.response.status
+      );
+    } else {
+      errorResponse(res, "创建图表失败", 500);
+    }
+  }
+};

+ 26 - 0
downLoadServer/src/server/controllers/exampleController.js

@@ -0,0 +1,26 @@
+import { successResponse, errorResponse } from "../utils/response.js";
+
+export const getExample = (req, res) => {
+  try {
+    const data = {
+      message: "这是测试数据",
+    };
+    successResponse(res, data);
+  } catch (error) {
+    errorResponse(res, "获取数据失败", 500);
+  }
+};
+
+export const createExample = (req, res) => {
+  try {
+    if (!req.body.name) {
+      errorResponse(res, "名称不能为空", 400);
+      return;
+    }
+
+    const data = req.body;
+    successResponse(res, data, "实例创建成功", 201);
+  } catch (error) {
+    errorResponse(res, "创建实例失败", 500);
+  }
+};

+ 12 - 0
downLoadServer/src/server/middleware/errorHandler.js

@@ -0,0 +1,12 @@
+export const errorHandler = (err, req, res, next) => {
+  console.error(err.stack);
+
+  const statusCode = err.statusCode || 500;
+  const message = err.message || "Internal Server Error";
+
+  res.status(statusCode).json({
+    status: "error",
+    statusCode,
+    message,
+  });
+};

+ 10 - 0
downLoadServer/src/server/middleware/logger.js

@@ -0,0 +1,10 @@
+export const logger = (req, res, next) => {
+  const start = Date.now();
+
+  res.on("finish", () => {
+    const duration = Date.now() - start;
+    console.log(`${req.method} ${req.url} ${res.statusCode} ${duration}ms`);
+  });
+
+  next();
+};

+ 8 - 0
downLoadServer/src/server/routes/chartRoutes.js

@@ -0,0 +1,8 @@
+import express from "express";
+import { createBarChart } from "../controllers/chartController.js";
+
+const router = express.Router();
+
+router.post("/bar", createBarChart);
+
+export default router;

+ 9 - 0
downLoadServer/src/server/routes/exampleRoutes.js

@@ -0,0 +1,9 @@
+import express from "express";
+import { getExample, createExample } from "../controllers/exampleController.js";
+
+const router = express.Router();
+
+router.get("/", getExample);
+router.post("/", createExample);
+
+export default router;

+ 42 - 0
downLoadServer/src/server/server.js

@@ -0,0 +1,42 @@
+import express from "express";
+import { serverConfig } from "./config.js";
+import { logger } from "./middleware/logger.js";
+import { errorHandler } from "./middleware/errorHandler.js";
+import exampleRoutes from "./routes/exampleRoutes.js";
+import chartRoutes from "./routes/chartRoutes.js";
+import path from "path";
+import { fileURLToPath } from "url";
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+const app = express();
+
+// 中间件
+app.use(express.json());
+app.use(logger);
+
+// 静态文件服务
+app.use("/images", express.static(path.join(process.cwd(), "src", "images")));
+app.use("/js", express.static(path.join(process.cwd(), "src", "public", "js")));
+app.use(
+  "/testData.json",
+  express.static(path.join(process.cwd(), "src", "testData.json"))
+);
+
+// 路由
+app.use("/api/examples", exampleRoutes);
+app.use("/api/charts", chartRoutes);
+
+// 错误处理
+app.use(errorHandler);
+
+export const startServer = () => {
+  app.listen(serverConfig.port, serverConfig.host, () => {
+    console.log(
+      `Server is running on http://${serverConfig.host}:${serverConfig.port}`
+    );
+  });
+};
+
+export default app;

+ 209 - 0
downLoadServer/src/server/utils/chartsCom/BarChart.js

@@ -0,0 +1,209 @@
+import puppeteer from "puppeteer";
+import fs from "fs-extra";
+import path from "path";
+import axios from "axios";
+import FormData from "form-data";
+
+/**
+ * 生成柱状图并上传
+ * @param {Object} data - 图表数据
+ * @returns {Promise<String>} - 返回图片URL
+ */
+export const generateBarChart = async (data) => {
+  try {
+    console.log("开始生成图表...");
+    console.log("数据:", data);
+
+    // 创建临时目录
+    const tempDir = path.join(process.cwd(), "temp");
+    await fs.ensureDir(tempDir);
+    const tempFilePath = path.join(tempDir, `temp_chart_${Date.now()}.png`);
+
+    // 获取 plotly.js 的绝对路径
+    const plotlyPath = path.join(
+      process.cwd(),
+      "src",
+      "public",
+      "js",
+      "plotly-latest.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];
+      const trace = {
+        x: chartDataset.xData,
+        y: chartDataset.yData,
+        type: "bar",
+        marker: {
+          color: "#588CF0",
+        },
+        line: {
+          color: "#588CF0",
+        },
+        name: chartDataset.title || "数据",
+        hovertemplate: `${data.xaixs}: %{x} <br> ${data.yaixs}: %{y} <br>`,
+      };
+
+      // 准备布局配置
+      const layout = {
+        title: {
+          text: chartDataset.title,
+          font: {
+            size: 16,
+            weight: "bold",
+          },
+        },
+        xaxis: {
+          title: data.xaixs || "X轴",
+          gridcolor: "rgb(255,255,255)",
+          type: data.xaixs === "机组" ? "category" : undefined,
+          tickcolor: "rgb(255,255,255)",
+          backgroundcolor: "#e5ecf6",
+        },
+        yaxis: {
+          title: data.yaixs || "Y轴",
+          gridcolor: "rgb(255,255,255)",
+          tickcolor: "rgb(255,255,255)",
+          backgroundcolor: "#e5ecf6",
+          title_standoff: 100,
+        },
+        margin: {
+          l: 50,
+          r: 50,
+          t: 50,
+          b: 50,
+        },
+        plot_bgcolor: "#e5ecf6",
+        gridcolor: "#fff",
+        bgcolor: "#e5ecf6",
+        autosize: true,
+      };
+
+      // 如果 Y 轴是 "温度偏差",添加两条红色虚线
+      if (data.yaixs === "温度偏差") {
+        layout.shapes = [
+          {
+            type: "line",
+            xref: "paper",
+            yref: "y",
+            x0: 0,
+            x1: 1,
+            y0: 5,
+            y1: 5,
+            line: {
+              color: "red",
+              width: 2,
+              dash: "dash",
+            },
+            hovertext: "上限: 5°C",
+            hoverinfo: "text",
+          },
+          {
+            type: "line",
+            xref: "paper",
+            yref: "y",
+            x0: 0,
+            x1: 1,
+            y0: -5,
+            y1: -5,
+            line: {
+              color: "red",
+              width: 2,
+              dash: "dash",
+            },
+            hovertext: "下限: -5°C",
+            hoverinfo: "text",
+          },
+        ];
+        layout.hovermode = "x unified";
+      }
+
+      // 如果是机组数据,设置刻度值
+      if (data.xaixs === "机组" || data.xaixs === "机组名称") {
+        layout.xaxis.tickvals = chartDataset.xData;
+        layout.xaxis.ticktext = chartDataset.xData;
+      }
+
+      // 创建HTML内容
+      const htmlContent = `
+        <!DOCTYPE html>
+        <html>
+          <head>
+            <script>${plotlyContent}</script>
+            <style>
+              body { margin: 0; }
+              #chart { width: 800px; height: 600px; }
+            </style>
+          </head>
+          <body>
+            <div id="chart"></div>
+            <script>
+              window.onload = function() {
+                const trace = ${JSON.stringify(trace)};
+                const layout = ${JSON.stringify(layout)};
+                Plotly.newPlot('chart', [trace], layout).then(() => {
+                  window.chartRendered = true;
+                });
+              };
+            </script>
+          </body>
+        </html>
+      `;
+
+      // 设置页面内容
+      await page.setContent(htmlContent, {
+        waitUntil: "networkidle0",
+      });
+
+      // 等待图表渲染完成
+      await page.waitForFunction(() => window.chartRendered === true, {
+        timeout: 60000,
+      });
+
+      // 截图并保存到临时文件
+      const chartElement = await page.$("#chart");
+      await chartElement.screenshot({
+        path: tempFilePath,
+        type: "png",
+      });
+
+      // 上传图片到服务器
+      const formData = new FormData();
+      formData.append("file", fs.createReadStream(tempFilePath));
+      formData.append("type", "chart");
+      formData.append("engineCode", data.engineCode);
+      formData.append("analysisTypeCode", data.analysisTypeCode);
+
+      const uploadResponse = await axios.post(
+        "http://192.168.50.233:6900/upload", //minio 地址上传
+        formData,
+        {
+          headers: {
+            ...formData.getHeaders(),
+          },
+        }
+      );
+
+      // 删除临时文件
+      await fs.remove(tempFilePath);
+
+      console.log("图表生成并上传成功:", uploadResponse.data.url);
+      return uploadResponse.data.url;
+    } finally {
+      await browser.close();
+    }
+  } catch (error) {
+    console.error("生成图表失败:", error);
+    throw error;
+  }
+};

+ 59 - 0
downLoadServer/src/server/utils/response.js

@@ -0,0 +1,59 @@
+/**
+ * 成功响应
+ * @param {Object} res - Express响应对象
+ * @param {Object} data - 响应数据
+ * @param {String} message - 响应消息
+ * @param {Number} statusCode - HTTP状态码
+ */
+export const successResponse = (
+  res,
+  data,
+  message = "Success",
+  statusCode = 200
+) => {
+  res.status(statusCode).json({
+    status: "success",
+    statusCode,
+    message,
+    data,
+  });
+};
+
+/**
+ * 错误响应
+ * @param {Object} res - Express响应对象
+ * @param {String} message - 错误消息
+ * @param {Number} statusCode - HTTP状态码
+ */
+export const errorResponse = (res, message = "Error", statusCode = 400) => {
+  res.status(statusCode).json({
+    status: "error",
+    statusCode,
+    message,
+  });
+};
+
+/**
+ * 分页响应
+ * @param {Object} res - Express响应对象
+ * @param {Object} data - 响应数据
+ * @param {Number} total - 总记录数
+ * @param {Number} page - 当前页码
+ * @param {Number} pageSize - 每页大小
+ */
+export const paginatedResponse = (res, data, total, page, pageSize) => {
+  res.status(200).json({
+    status: "success",
+    statusCode: 200,
+    message: "Success",
+    data: {
+      items: data,
+      pagination: {
+        total,
+        page,
+        pageSize,
+        totalPages: Math.ceil(total / pageSize),
+      },
+    },
+  });
+};

+ 32 - 0
downLoadServer/src/test.mjs

@@ -0,0 +1,32 @@
+import axios from "axios";
+
+const testChartGeneration = async () => {
+  try {
+    // 测试数据URL
+    // const dataUrl = "http://127.0.0.1:3000/testData.json";
+    const dataUrl =
+      "http://192.168.50.233:6900/wof039800012/WOF039800012-WOB000001/wind_speed_frequency/manual/wind_Speed_Frequency%2303.json";
+
+    console.log("开始测试图表生成...");
+
+    // 发送请求生成图表
+    const response = await axios.post("http://127.0.0.1:3000/api/charts/bar", {
+      dataUrl: dataUrl,
+    });
+
+    console.log("图表生成成功!");
+    console.log("响应数据:", response.data);
+    console.log(
+      "图表URL:",
+      `http://127.0.0.1:3000${response.data.data.imageUrl}`
+    );
+  } catch (error) {
+    console.error(
+      "测试失败:",
+      error.response ? error.response.data : error.message
+    );
+  }
+};
+
+// 运行测试
+testChartGeneration();

+ 13 - 0
downLoadServer/src/testData.json

@@ -0,0 +1,13 @@
+{
+  "data": [
+    {
+      "xData": ["机组1", "机组2", "机组3"],
+      "yData": [10, 20, 30],
+      "title": "机组性能对比"
+    }
+  ],
+  "chartType": "bar",
+  "color1": "rgb(55, 83, 109)",
+  "xaixs": "机组",
+  "yaixs": "性能指标"
+}

+ 1 - 2
src/views/home/Index.vue

@@ -50,9 +50,8 @@ export default {
   },
   data() {
     return {
-      // history: ["11", "22", "33"],
       loading: false,
-      isShowHd: true,
+      isShowHd: false, //true 则展示华电的菜单样式,false 则展示默认的左侧边栏菜单
     };
   },
   methods: {

+ 13 - 5
src/views/overview/components/production_indicator_unit/index.vue

@@ -1,7 +1,7 @@
 <!--
  * @Author: your name
  * @Date: 2025-01-13 13:44:56
- * @LastEditTime: 2025-03-21 14:40:25
+ * @LastEditTime: 2025-04-08 11:31:01
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/overview/components/production_indicator_unit/index.vue
@@ -148,6 +148,11 @@
                 :ref="itemind + 'chart' + chartInd"
                 :chartData="chantItem"
                 :itemCsvData="itemCsv.data"
+                v-if="
+                  (fieldEngineCodes.length > 0 &&
+                    searchFenitem.includes(chantItem.wind_turbine_name)) ||
+                  fieldEngineCodes.length === 0
+                "
               ></Rader>
             </el-col>
           </el-row>
@@ -280,6 +285,7 @@ export default {
       productionIndicatorCsvHeader: [],
       editableTabs: [],
       isShowDescription: false,
+      searchFenitem: [], //过滤风机图表
     };
   },
   computed: {
@@ -404,10 +410,6 @@ export default {
                     .filter((row) => Object.keys(row).length)
                     .slice(0, result.data.length - 1),
                 }); // 过滤空行
-                console.log(
-                  this.productionIndicatorCsvData,
-                  "this.productionIndicatorCsvData"
-                );
               },
               error: (error) => {
                 console.error("CSV 解析错误:", error);
@@ -495,8 +497,14 @@ export default {
     },
     //获取选中风机list
     getEnfineList(data, setUpImg) {
+      console.log(data, "获取选中风机list");
       this.fieldEngineCodes = data;
       this.setUpImgData = [...setUpImg];
+      this.windEngineGroupList.map((item) => {
+        if (data.includes(item.engineCode)) {
+          this.searchFenitem.push(item.engineName);
+        }
+      });
       this.getAnalysisDetail();
     },
     //下一条

+ 2 - 8
src/views/performance/components/chartsCom/BarChart.vue

@@ -1,7 +1,7 @@
 <!--
  * @Author: your name
  * @Date: 2024-09-11 14:30:17
- * @LastEditTime: 2025-03-21 16:56:16
+ * @LastEditTime: 2025-04-08 09:45:01
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/performance/components/chartsCom/BarChart.vue
@@ -66,13 +66,7 @@ export default {
         engineTypeName: "",
         xaixs: "",
         yaixs: "",
-        data: [
-          // {
-          //   xData: ["2024-01-01", "2024-01-02", "2024-01-03", "2024-01-04", "2024-01-05"],
-          //   yData: [10, 20, 30, 40, 50],
-          //   title: "数据集1",
-          // },
-        ],
+        data: [],
       },
       chartType: "bar", // 当前图表类型 ('bar' 或 'scatter')
       color1: "#588CF0", // 默认颜色

Деякі файли не було показано, через те що забагато файлів було змінено