Browse Source

经济运行分析报告模块完成

Koishi 1 year ago
parent
commit
47dd04aa70
7 changed files with 493 additions and 16 deletions
  1. 2 2
      package.json
  2. 17 10
      src/main.js
  3. 6 0
      src/router/index.js
  4. 42 0
      src/tools/fixGetPDF.js
  5. 10 4
      src/views/layout/Menu.vue
  6. 15 0
      src/views/report/psar.vue
  7. 401 0
      src/views/report/psarReport.vue

+ 2 - 2
package.json

@@ -11,10 +11,10 @@
     "fix-memory-limit": "cross-env LIMIT=4096 increase-memory-limit"
   },
   "dependencies": {
-    "@open-wc/webpack-import-meta-loader": "0.4.7",
     "@antv/x6": "^1.24.4",
     "@arcgis/core": "4.19.3",
-    "@element-plus/icons-vue": "^0.2.4",
+    "@element-plus/icons-vue": "^2.1.0",
+    "@open-wc/webpack-import-meta-loader": "0.4.7",
     "animate.css": "3.5",
     "axios": "^0.21.1",
     "cesium": "1.78.0",

+ 17 - 10
src/main.js

@@ -4,7 +4,6 @@ import router from "./router";
 import store from "./store";
 import "font-awesome/css/font-awesome.min.css";
 
-
 // 引入 element-ui
 import ElementPlus from "element-plus";
 import "element-plus/dist/index.css";
@@ -24,6 +23,8 @@ import basicTool from "@tools/basicTool";
 
 import animated from "animate.css";
 
+import * as ElementPlusIconsVue from "@element-plus/icons-vue";
+
 /**
  * 对 Date 的扩展,将 Date 转化为指定格式的字符串
  * @param {String} fmt 传入一个字符串,根据所传字符串的格式返回转换后特定格式的日期。
@@ -51,15 +52,21 @@ Date.prototype.formatDate = function (fmt) {
   return fmt;
 }
 
-window.__STATICVUE__ = createApp(App);
-window.__STATICVUE__.use(ElementPlus, { locale });
-window.__STATICVUE__.use(store);
-window.__STATICVUE__.use(router);
-window.__STATICVUE__.use(animated);
+const app = createApp(App);
+
+for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+  app.component(key, component);
+}
+
+app.use(ElementPlus, { locale });
+app.use(store);
+app.use(router);
+app.use(animated);
 
-window.__STATICVUE__.config.globalProperties.API = axios; //全局注册
-window.__STATICVUE__.config.globalProperties.BASE = basicTool; //全局注册
+app.config.globalProperties.API = axios; //全局注册
+app.config.globalProperties.BASE = basicTool; //全局注册
 
-// window.__STATICVUE__.config.globalProperties.$Cesium = Cesium;
+// app.config.globalProperties.$Cesium = Cesium;
 
-window.__STATICVUE__.mount('#app');
+app.mount('#app');
+window.__STATICVUE__ = app;

+ 6 - 0
src/router/index.js

@@ -375,6 +375,12 @@ const routes = [{
 	component: () =>
 		import( /* webpackChunkName: "xzdl" */ "../views/report/xzdl.vue"),
 },
+{
+	path: "/decision/psar",//修正预测风速电量
+	name: "psar",
+	component: () =>
+		import( /* webpackChunkName: "psar" */ "../views/report/psar.vue"),
+},
 /***********************************************************经济运行************************************************************* */
 /***********************************************************智慧检修************************************************************* */
 

+ 42 - 0
src/tools/fixGetPDF.js

@@ -0,0 +1,42 @@
+import html2Canvas from 'html2canvas';
+import jsPDF from 'jspdf';
+export default {
+    // eslint-disable-next-line func-names
+    getPdf(dom, title) {
+        html2Canvas(dom, {
+            useCORS: true,//解决网络图片跨域问题
+            width: dom.width,
+            height: dom.height,
+            windowWidth: dom.scrollWidth,
+            dpi: window.devicePixelRatio * 4, // 将分辨率提高到特定的DPI 提高四倍
+            scale: 4, // 按比例增加分辨率
+        }).then((canvas) => {
+            // eslint-disable-next-line new-cap
+            const pdf = new jsPDF('p', 'mm', 'a4'); // A4纸,纵向
+            const ctx = canvas.getContext('2d');
+            const a4w = 170;
+            const a4h = 257; // A4大小,210mm x 297mm,四边各保留20mm的边距,显示区域170x257
+            const imgHeight = Math.floor(a4h * canvas.width / a4w); // 按A4显示比例换算一页图像的像素高度
+            let renderedHeight = 0;
+
+            while (renderedHeight < canvas.height) {
+                const page = document.createElement('canvas');
+                page.width = canvas.width;
+                page.height = Math.min(imgHeight, canvas.height - renderedHeight);// 可能内容不足一页
+
+                // 用getImageData剪裁指定区域,并画到前面创建的canvas对象中
+                page.getContext('2d').putImageData(ctx.getImageData(0, renderedHeight, canvas.width, Math.min(imgHeight, canvas.height - renderedHeight)), 0, 0);
+                pdf.addImage(page.toDataURL('image/jpeg', 1.0), 'JPEG', 20, 20, a4w, Math.min(a4h, a4w * page.height / page.width)); // 添加图像到页面,保留10mm边距
+
+                renderedHeight += imgHeight;
+                if (renderedHeight < canvas.height) {
+                    pdf.addPage();// 如果后面还有内容,添加一个空页
+                }
+                // 预览pdf(这里我用的是事件总线把canvas传递过去展示,达到模拟pdf预览的效果,有用但效果不是很好,有需要的可以自行修改)
+                //this.$EventBus.$emit('open-pdf', canvas);
+            }
+            // 保存文件
+            pdf.save(`${title}.pdf`);
+        });
+    },
+}

+ 10 - 4
src/views/layout/Menu.vue

@@ -337,12 +337,12 @@ export default {
               icon: "svg-gffx",
               path: "/decision/loadRate",
               children: [
-              {
+                {
                   text: "光伏负荷率",
                   icon: "svg-wind-site",
                   path: "/decision/loadRate",
                 },
-              ]
+              ],
             },
             {
               text: "气象分析",
@@ -366,6 +366,12 @@ export default {
                 },
               ],
             },
+            {
+              text: "经济运行分析报告",
+              // icon: "svg-qxfx",
+              icon: "svg-能效分析",
+              path: "/decision/psar",
+            },
             // {
             //   text: "单机分析",
             //   icon: "svg-wind-site",
@@ -1015,8 +1021,8 @@ export default {
 </script>
  
 <style lang="less">
-.tip-set{
-  z-index: 9999!important;
+.tip-set {
+  z-index: 9999 !important;
 }
 .menu {
   padding-top: 1.481vh;

+ 15 - 0
src/views/report/psar.vue

@@ -0,0 +1,15 @@
+<template>
+  <PsarReport />
+</template>
+
+<script>
+import PsarReport from "./psarReport.vue";
+export default {
+  components: {
+    PsarReport,
+  },
+  data() {
+    return {};
+  },
+};
+</script>

+ 401 - 0
src/views/report/psarReport.vue

@@ -0,0 +1,401 @@
+<template>
+  <div class="reportBox">
+    <div class="outlineBox">
+      <div class="btnBox">
+        <el-button type="success" size="mini" @click="exportPDF"
+          >导出PDF</el-button
+        >
+      </div>
+      <template v-if="reportTitle">
+        <p
+          class="songti font fw"
+          style="
+            width: 100%;
+            padding: 12px 24px;
+            color: #b3bdc0;
+            background: rgba(96, 103, 105, 0.2);
+            cursor: pointer;
+            margin: 0;
+            color: #05bb4c;
+          "
+          @click="
+            nodeClick({
+              label: `国家能源集团宁夏新能源开发有限公司${reportTitle}`,
+            })
+          "
+        >
+          国家能源集团宁夏新能源开发有限公司{{ reportTitle }}
+        </p>
+      </template>
+      <el-tree
+        :data="treeData"
+        :props="{
+          children: 'children',
+          label: 'label',
+        }"
+        style="height: calc(100% - 62px - 40px); overflow-y: scroll"
+        @node-click="nodeClick"
+      >
+        <template #default="{ node, data }">
+          <span class="font songti fw">{{ data.index }}{{ node.label }}</span>
+        </template>
+      </el-tree>
+    </div>
+    <div class="resizeLine">
+      <el-divider direction="vertical" />
+      <el-icon :size="24" color="#eee"><DCaret /></el-icon>
+    </div>
+    <div class="contentBox">
+      <div class="pagePadding A4">
+        <div class="page">
+          <template v-if="reportTitle">
+            <h1
+              :id="`国家能源集团宁夏新能源开发有限公司${reportTitle}`"
+              class="songti mainTitle fw"
+              style="margin-bottom: 0"
+            >
+              国家能源集团宁夏新能源开发有限公司
+            </h1>
+            <h1 :id="reportTitle" class="songti mainTitle fw">
+              {{ reportTitle }}
+            </h1>
+          </template>
+          <el-card
+            :id="pItem.label"
+            v-for="pItem in treeData"
+            :key="pItem.index"
+          >
+            <p class="songti title fw">
+              {{ pItem.index || "" }}{{ pItem.label }}
+            </p>
+            <p
+              class="fangsong indent"
+              style="margin-bottom: 5px"
+              v-for="(cItem, cIndex) in pItem.content"
+              :key="cIndex"
+            >
+              {{ cItem }}
+            </p>
+          </el-card>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import Get_PDF from "@tools/fixGetPDF";
+export default {
+  components: {},
+
+  data() {
+    return {
+      locationHref: "",
+      reportTitle: "",
+      treeData: [
+        {
+          label: "发电量完成情况",
+          children: [],
+          content: [],
+        },
+        {
+          label: "资源及理论发电量平衡分析",
+          children: [],
+          content: [],
+        },
+        {
+          label: "电网调度因素损失分析",
+          children: [],
+          content: [],
+        },
+        {
+          label: "故障及损失分析",
+          children: [],
+          content: [],
+        },
+        {
+          label: "计划检修损失分析",
+          children: [],
+          content: [],
+        },
+        {
+          label: "性能损失分析",
+          children: [],
+          content: [],
+        },
+        {
+          label: "厂用电量分析",
+          children: [],
+          content: [],
+        },
+        {
+          label: "场站问题总结和建议",
+          children: [],
+          content: [],
+        },
+      ],
+    };
+  },
+
+  created() {
+    this.locationHref = location.href;
+    let treeData = this.initTreeData(this.BASE.deepCopy(this.treeData || []));
+    this.treeData = treeData;
+  },
+
+  mounted() {
+    this.$nextTick(() => {
+      this.initMouseEvent();
+      this.getContentData();
+      if (this.$route.hash) {
+        this.pageScroll(this.$route.hash.replace(/^\#/, ""));
+      }
+    });
+  },
+
+  methods: {
+    // 初始化树形
+    initTreeData(treeData, parentIdx = "") {
+      treeData.forEach((ele, idx) => {
+        ele.index = `${parentIdx}${idx + 1}.`;
+        if (ele.children?.length) {
+          return this.initTreeData(ele.children, ele.index);
+        }
+      });
+      return treeData;
+    },
+
+    // 初始化鼠标事件
+    initMouseEvent() {
+      const resize = document.querySelector(".resizeLine");
+      const left = document.querySelector(".outlineBox");
+      const right = document.querySelector(".contentBox");
+      const box = document.querySelector(".reportBox");
+      const svgPath = resize.querySelector(".el-icon path");
+
+      left.style.width = getComputedStyle(left, null).width;
+      right.style.width = getComputedStyle(right, null).width;
+      resize.onmousedown = (e) => {
+        const startX = e.clientX;
+        resize.left = resize.offsetLeft;
+        svgPath.style.fill = "#05bb4c";
+        svgPath.style.transform = "scale(1.5)";
+        svgPath.style.transformOrigin = "center center";
+        svgPath.style.transition = "0.2s";
+        document.onmousemove = (e) => {
+          var endX = e.clientX;
+          const maxT = box.clientWidth - resize.offsetWidth;
+          let moveLen = resize.left + (endX - startX);
+          if (moveLen < 250) moveLen = 250;
+          if (moveLen > maxT - 1000) moveLen = maxT - 1000;
+
+          resize.style.left = moveLen;
+          left.style.width = `${moveLen}px`;
+          right.style.width = `${box.clientWidth - moveLen - 8}px`;
+        };
+        document.onmouseup = () => {
+          svgPath.style.fill = "#eee";
+          svgPath.style.transform = "scale(1)";
+          svgPath.style.transformOrigin = "center center";
+          svgPath.style.transition = "0.2s";
+          document.onmousemove = null;
+          document.onmouseup = null;
+          resize.releaseCapture && resize.releaseCapture();
+        };
+        resize.setCapture && resize.setCapture();
+        return false;
+      };
+    },
+
+    // 树形点击
+    nodeClick(node) {
+      const label = node.label;
+      location.hash = `${this.$route.path}#${label}`;
+      this.pageScroll(label);
+    },
+
+    // 页面滚动至锚点
+    pageScroll(id) {
+      document.getElementById(id)?.scrollIntoView({ behavior: "smooth" });
+    },
+
+    // 数据请求
+    getContentData() {
+      const that = this;
+      that.API.requestData({
+        isMust: false, // 请求是否携带 token ,默认为 true ,可缺省
+        showLoading: true, // 请求是否显示加载中遮罩层,默认 false ,可缺省
+        method: "GET", // 请求方式,默认为 GET ,可缺省
+        baseURL: "http://192.168.10.9:9001/", // 请求服务器地址 + 端口,可缺省
+        subUrl: "economy/analysis/2023/3", // 请求接口地址,必传项
+        timeout: 60000, // 请求超时时间,默认 3s ,可缺省
+        success({ data }) {
+          that.treeData.forEach((pEle) => {
+            const content = data[pEle.label] || [];
+            if (content.length) {
+              content.forEach((cEle) => {
+                pEle.content.push(cEle);
+              });
+            }
+          });
+          that.reportTitle = data?.["标题"]?.[0] || "";
+        },
+      });
+    },
+
+    // 导出PDF
+    exportPDF() {
+      this.BASE.showLoading({
+        text: "正在导出...请稍后...",
+      });
+      setTimeout(() => {
+        Get_PDF.getPdf(
+          document.querySelector(".contentBox .page"),
+          "健康评价报告概述"
+        );
+        this.BASE.closeLoading();
+      }, 50);
+    },
+  },
+
+  watch: {},
+};
+</script>
+<style lang="less" scoped>
+.reportBox {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+
+  .outlineBox {
+    width: calc(25% - 8px);
+    height: 100%;
+    float: left;
+
+    .btnBox {
+      width: 97%;
+      margin: 0 1.5% 12px 1.5%;
+      display: flex;
+      justify-content: flex-end;
+      align-items: center;
+    }
+  }
+
+  .resizeLine {
+    width: 6px;
+    height: 100%;
+    cursor: w-resize;
+    float: left;
+    position: relative;
+
+    .el-icon {
+      position: absolute;
+      left: calc(50% - 12px);
+      top: calc(50% - 12px);
+      transform: rotate(90deg);
+      transition: 0.2s;
+    }
+
+    .el-divider--vertical {
+      width: 2px;
+      margin: 0 2px;
+      height: 100%;
+      transition: 0.2s;
+    }
+
+    &:active .el-divider--vertical {
+      background: #05bb4c;
+      transition: 0.2s;
+    }
+  }
+
+  .contentBox {
+    float: right;
+    width: 75%;
+    height: 100%;
+    display: flex;
+    justify-content: center;
+    align-items: flex-start;
+    overflow-y: scroll;
+
+    .mainTitle {
+      font-size: 30px;
+      width: 90%;
+      margin: 0 5% 20px 5%;
+      text-align: center;
+      color: #000;
+    }
+
+    .page {
+      background: #fff;
+    }
+
+    .pagePadding {
+      padding: 20mm;
+      box-sizing: border-box;
+      background: #fff;
+    }
+
+    .A4 {
+      width: 210mm;
+    }
+
+    .el-card {
+      border-color: transparent;
+      color: #000;
+
+      .el-card__body {
+        p {
+          margin: 0;
+        }
+      }
+    }
+  }
+}
+
+.font {
+  width: 100%;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  line-height: 1.5;
+}
+
+.fw {
+  font-weight: 700;
+}
+
+.songti {
+  font-family: SimSun, 宋体;
+  font-size: 18.7px;
+  line-height: 2;
+}
+
+.fangsong {
+  font-family: FangSong, 仿宋;
+  font-size: 18.7px;
+  line-height: 2;
+}
+
+.indent {
+  text-indent: 2em;
+}
+
+.title {
+  font-size: 22px;
+  line-height: 2;
+}
+</style>
+
+<style lang="less">
+.contentBox {
+  .el-card {
+    .el-card__body {
+      padding: 0;
+    }
+  }
+  .el-card.is-always-shadow {
+    box-shadow: none;
+  }
+}
+</style>