瀏覽代碼

利用小时分析、经济运行报告、品牌对标完成,修改箭头样式

baiyanting 2 年之前
父節點
當前提交
200638e87c

+ 8 - 0
src/api/curveAnalyse.js

@@ -162,3 +162,11 @@ export function zhcydlList(data) {
         method: 'get',
     })
 }
+//专题分析-利用小时分析
+export function lyxsList(data) {
+    return request({
+      baseURL: process.env.VUE_APP_Economy,
+      url: `/specific/Utilization?companys=${data.companys}&type=${data.type}&year=${data.year}`,
+      method: "get",
+    });
+}

+ 9 - 0
src/api/monthlyPerformanceAnalysis.js

@@ -395,3 +395,12 @@ export function getApiCleanAnalyseDetails(params) {
     method: "GET",
   });
 }
+// ------------------------------------------------------------经济运行分析报告------------------------------------------------------------------------
+export function getApiEconReport(params) {
+  return request({
+    baseURL: process.env.VUE_APP_Economy,
+    url: `/economicsOperation/analysis?wpid=${params.wpid}&year=${params.year}&month=${params.month}`,
+    method: "GET",
+  });
+}
+

+ 8 - 0
src/api/performance.js

@@ -143,3 +143,11 @@ export function sbdb(data) {
     method: "get",
   });
 }
+//品牌对标
+export function ppdb(data) {
+  return request({
+    baseURL: process.env.VUE_APP_Economy,
+    url: `benchmarking/ppdb?companys=${data.companys}&type=${data.type}&wpids=${data.wpids}&model=${data.model}&beginDate=${data.beginDate}&endDate=${data.endDate}&target=&sort=`,
+    method: "get",
+  });
+}

+ 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`);
+        });
+    },
+}

+ 512 - 0
src/views/layout/economicsOperation/analyse/economyReport/index.vue

@@ -0,0 +1,512 @@
+<template>
+  <div class="reportBox">
+    <div class="outlineBox">
+      <div class="btnBox">
+        <el-dropdown
+          trigger="click"
+          @command="callFn"
+          popper-class="psarDropdown"
+        >
+          <el-button style="color: #05bb4c" size="small" type="text"
+            >导出
+            <el-icon><arrow-down /></el-icon>
+          </el-button>
+          <template #dropdown>
+            <el-dropdown-menu>
+              <el-dropdown-item command="exportPDF">PDF</el-dropdown-item>
+              <el-dropdown-item command="exportWord">WORD</el-dropdown-item>
+            </el-dropdown-menu>
+          </template>
+        </el-dropdown>
+        <!-- <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% - 48px); 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="`${reportName}${reportTitle}`"
+              class="songti mainTitle fw"
+              style="margin-bottom: 0"
+            >
+              {{ currentCompany }}{{ reportName }}
+            </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";
+import docxtemplater from "docxtemplater";
+import { saveAs } from "file-saver";
+import JSZipUtils from "jszip-utils";
+import PizZip from "pizzip";
+import { getApiEconReport } from "@/api/monthlyPerformanceAnalysis.js";
+export default {
+  name: "EconomyReport",
+  components: {},
+
+  data() {
+    return {
+      locationHref: "",
+      reportTitle: "",
+      reportName: "",
+      treeData: [
+        {
+          label: "发电量完成情况",
+          children: [],
+          content: [],
+        },
+        {
+          label: "资源及理论发电量平衡分析",
+          children: [],
+          content: [],
+        },
+
+        {
+          label: "故障及损失分析",
+          children: [],
+          content: [],
+        },
+        {
+          label: "性能损失分析",
+          children: [],
+          content: [],
+        },
+        {
+          label: "厂用电量分析",
+          children: [],
+          content: [],
+        },
+        {
+          label: "场站问题总结和建议",
+          children: [],
+          content: [],
+        },
+      ],
+      sourceData: {},
+      currentWpid: "",
+      currentCompany: "",
+      currentYear: "",
+      currentMonth: "",
+    };
+  },
+
+  created() {
+    this.locationHref = location.href;
+    let treeData = this.initTreeData(
+      JSON.parse(JSON.stringify(this.treeData || []))
+    );
+    this.treeData = treeData;
+  },
+
+  mounted() {
+    this.$nextTick(() => {
+      this.initMouseEvent();
+      if (this.$route.hash) {
+        this.pageScroll(this.$route.hash.replace(/^\#/, ""));
+      }
+    });
+  },
+
+  methods: {
+    init(row) {
+      this.reportName = row.stationName;
+      this.getContentData();
+    },
+    // 初始化树形
+    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() {
+      let params = {
+        wpid: this.currentWpid,
+        year: this.currentYear,
+        month: this.currentMonth,
+      };
+      getApiEconReport(params).then(({ data }) => {
+        this.sourceData = data;
+        this.treeData.forEach((pEle) => {
+          const content = data[pEle.label] || [];
+          if (content.length) {
+            content.forEach((cEle) => {
+              pEle.content.push(cEle);
+            });
+          }
+        });
+        this.reportTitle = data?.["标题"]?.[0] || "";
+      });
+    },
+
+    // 下拉列表调用函数
+    callFn(command) {
+      this[command]();
+    },
+
+    // 导出 PDF
+    exportPDF() {
+      this.BASE.showLoading({
+        text: "正在导出...请稍后...",
+      });
+      setTimeout(() => {
+        Get_PDF.getPdf(
+          document.querySelector(".contentBox .page"),
+          "2023年3月经济运行分析报告"
+        );
+        this.BASE.closeLoading();
+      }, 50);
+    },
+
+    // 导出 WORD
+    exportWord() {
+      let wordData = {};
+      for (let key in this.sourceData) {
+        if (key === "标题") {
+          wordData[key] = this.sourceData[key][0];
+        } else {
+          let dataItem = [];
+          this.sourceData[key].forEach((content) => {
+            dataItem.push({ content });
+          });
+          wordData[key] = dataItem;
+        }
+      }
+      // 读取并获得模板文件的二进制内容
+      JSZipUtils.getBinaryContent(
+        "./static/template/wordTemplate.docx",
+        function (error, content) {
+          if (error) {
+            throw error;
+          }
+
+          // 创建一个PizZip实例,内容为模板的内容
+          let zip = new PizZip(content);
+          // 创建并加载docxtemplater实例对象
+          let doc = new docxtemplater(zip, { linebreaks: true });
+          // doc.attachModule(new ImageModule(opts));
+
+          doc.setData(wordData);
+
+          try {
+            // 用模板变量的值替换所有模板变量
+            doc.render();
+          } catch (error) {
+            // 抛出异常
+            let e = {
+              message: error.message,
+              name: error.name,
+              stack: error.stack,
+              properties: error.properties,
+            };
+            throw error;
+          }
+
+          // 生成一个代表docxtemplater对象的zip文件(不是一个真实的文件,而是在内存中的表示)
+          let out = doc.getZip().generate({
+            type: "blob",
+            mimeType:
+              "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+          });
+          // 将目标文件对象保存为目标类型的文件,并命名
+          saveAs(out, "2023年3月经济运行分析报告.docx");
+        }
+      );
+    },
+  },
+
+  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;
+  }
+}
+
+.el-popper.is-light.psarDropdown {
+  background: rgb(22, 30, 30);
+  border: 1px solid #05bb4c;
+
+  .el-scrollbar__wrap > ul {
+    padding: 0;
+  }
+
+  .el-dropdown-menu {
+    padding: 0;
+    background: none;
+  }
+
+  .el-dropdown-menu__item {
+    color: #fff;
+  }
+
+  .el-dropdown-menu__item:hover {
+    color: #05bb4c;
+  }
+
+  .el-popper__arrow {
+    &::before {
+      border-top-color: #05bb4c !important;
+      border-left-color: #05bb4c !important;
+    }
+  }
+}
+</style>

+ 43 - 0
src/views/layout/economicsOperation/analyse/stationAnalyse/index.vue

@@ -101,6 +101,7 @@
           height="48vh"
           ref="fitting_table"
           style="width: 100%"
+          @row-click="handleRowClick"
         >
           <el-table-column
             v-for="(item, index) in tableHeader"
@@ -128,6 +129,32 @@
         />
       </div>
     </div>
+    <el-dialog
+      class="dialogs"
+      width="85%"
+      top="8vh"
+      v-model="dialogCurveVisible"
+      :show-close="true"
+    >
+      <template #title>
+        <div class="dialog-title">
+          <img
+            class="dialog-title-img"
+            src="@/assets/img/images/dialog-title.png"
+          />
+          <div class="title">{{ dialogTitle }}</div>
+          <!--                    <i class="el-icon-full-screen"  @click="c"></i>-->
+        </div>
+      </template>
+      <div class="dialog-body" style="height: 65vh; width: 100%">
+        <EconomyReport ref="economyReport" />
+        <img
+          class="dialog-img"
+          style="bottom: 65px"
+          src="@/assets/img/images/dialog.png"
+        />
+      </div>
+    </el-dialog>
   </div>
 </template>
 
@@ -137,6 +164,7 @@ import { GetOrganization, GetStationByCompany } from "@/api/headerNav.js";
 import utils from "@/utils/downXlsx";
 import dayjs from "dayjs";
 import doubleYAxisLineChart from "@/components/chart/line/double-yAxis-line-chart.vue";
+import EconomyReport from "../economyReport/index.vue";
 export default {
   name: "stationAnalyse", //场站性能分析
   data() {
@@ -161,10 +189,13 @@ export default {
         { id: -1, name: "风电" },
         { id: -2, name: "光伏" },
       ],
+      dialogCurveVisible: false,
+      dialogTitle: "分析报告",
     };
   },
   components: {
     doubleYAxisLineChart,
+    EconomyReport,
   },
   created() {
     this.starTime = dayjs().startOf("day").format("YYYY-MM-DD HH:mm:ss");
@@ -244,6 +275,18 @@ export default {
         },
       ];
     },
+    handleRowClick(row) {
+      this.dialogCurveVisible = true;
+      this.$nextTick(() => {
+        this.$refs.economyReport.currentYear = dayjs(row.time).format("YYYY");
+        this.$refs.economyReport.currentMonth = dayjs(row.time).format("MM");
+        this.$refs.economyReport.currentWpid = this.stationVal;
+        this.$refs.economyReport.currentCompany = this.companyOptions.find(
+          (item) => item.id == this.companyVal
+        ).aname;
+        this.$refs.economyReport.init(row);
+      });
+    },
     downXlsxFn() {
       let header = [];
       this.tableHeader.forEach((it) => {

+ 40 - 0
src/views/layout/economicsOperation/analyse/stationAnalyse/indexFd.vue

@@ -101,6 +101,7 @@
           height="48vh"
           ref="fitting_table"
           style="width: 100%"
+          @row-click="handleRowClick"
         >
           <el-table-column
             v-for="(item, index) in tableHeader"
@@ -128,6 +129,32 @@
         />
       </div>
     </div>
+    <el-dialog
+      class="dialogs"
+      width="85%"
+      top="8vh"
+      v-model="dialogCurveVisible"
+      :show-close="true"
+    >
+      <template #title>
+        <div class="dialog-title">
+          <img
+            class="dialog-title-img"
+            src="@/assets/img/images/dialog-title.png"
+          />
+          <div class="title">{{ dialogTitle }}</div>
+          <!--                    <i class="el-icon-full-screen"  @click="c"></i>-->
+        </div>
+      </template>
+      <div class="dialog-body" style="height: 65vh; width: 100%">
+        <EconomyReport ref="economyReport" />
+        <img
+          class="dialog-img"
+          style="bottom: 65px"
+          src="@/assets/img/images/dialog.png"
+        />
+      </div>
+    </el-dialog>
   </div>
 </template>
 
@@ -137,6 +164,7 @@ import { GetOrganization, GetStationByCompany } from "@/api/headerNav.js";
 import utils from "@/utils/downXlsx";
 import dayjs from "dayjs";
 import doubleYAxisLineChart from "@/components/chart/line/double-yAxis-line-chart.vue";
+import EconomyReport from "../economyReport/index.vue";
 export default {
   name: "stationAnalyse", //场站性能分析
   data() {
@@ -161,10 +189,13 @@ export default {
         { id: -1, name: "风电" },
         { id: -2, name: "光伏" },
       ],
+      dialogCurveVisible: false,
+      dialogTitle: "分析报告",
     };
   },
   components: {
     doubleYAxisLineChart,
+    EconomyReport,
   },
   created() {
     this.starTime = dayjs().startOf("day").format("YYYY-MM-DD HH:mm:ss");
@@ -244,6 +275,15 @@ export default {
         },
       ];
     },
+    handleRowClick(row) {
+      this.dialogCurveVisible = true;
+      this.$nextTick(() => {
+        this.$refs.economyReport.currentYear = dayjs(row.time).format("YYYY");
+        this.$refs.economyReport.currentMonth = dayjs(row.time).format("MM");
+        this.$refs.economyReport.currentWpid = this.stationVal;
+        this.$refs.economyReport.init();
+      });
+    },
     downXlsxFn() {
       let header = [];
       this.tableHeader.forEach((it) => {

File diff suppressed because it is too large
+ 1202 - 0
src/views/layout/economicsOperation/benchmarkingManagement/brandBenchmarking/index.vue


+ 6 - 16
src/views/layout/economicsOperation/thematicAnalysis/generation/index.vue

@@ -364,6 +364,12 @@ export default {
   background-color: rgba(0, 0, 0, 0.45);
   border-radius: 5px;
   padding: 5px;
+  .icon-arrow-down:before {
+    color: #ff8300;
+  }
+  .icon-arrow-up:before {
+    color: #1c99ff;
+  }
 }
 
 .parcel-box {
@@ -489,22 +495,6 @@ export default {
   height: 0;
 }
 
-.compare-lift-down {
-  margin-left: 7px;
-  font-size: 14px;
-  font-family: Arial;
-  font-weight: 400;
-  color: #ff8300;
-}
-
-.compare-lift-up {
-  margin-left: 3px;
-  font-size: 14px;
-  font-family: Arial;
-  font-weight: 400;
-  color: #1c99ff;
-}
-
 .chart-name {
   display: flex;
   align-items: center;

+ 6 - 16
src/views/layout/economicsOperation/thematicAnalysis/generation/indexFd.vue

@@ -366,6 +366,12 @@ export default {
   background-color: rgba(0, 0, 0, 0.45);
   border-radius: 5px;
   padding: 5px;
+  .icon-arrow-down:before {
+    color: #ff8300;
+  }
+  .icon-arrow-up:before {
+    color: #1c99ff;
+  }
 }
 
 .parcel-box {
@@ -491,22 +497,6 @@ export default {
   height: 0;
 }
 
-.compare-lift-down {
-  margin-left: 7px;
-  font-size: 14px;
-  font-family: Arial;
-  font-weight: 400;
-  color: #ff8300;
-}
-
-.compare-lift-up {
-  margin-left: 7px;
-  font-size: 14px;
-  font-family: Arial;
-  font-weight: 400;
-  color: #1c99ff;
-}
-
 .chart-name {
   display: flex;
   align-items: center;

+ 478 - 0
src/views/layout/economicsOperation/thematicAnalysis/utillHours/index.vue

@@ -0,0 +1,478 @@
+<template>
+  <div class="parcel-box">
+    <div class="failure-header">
+      <div class="search">
+        <div class="search-left">
+          <el-select
+            size="mini"
+            v-model="tabIndex"
+            placeholder="请选择"
+            @change="tabClick"
+          >
+            <el-option
+              v-for="item in tabOptions"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            >
+            </el-option>
+          </el-select>
+          <el-select
+            size="mini"
+            v-model="company"
+            placeholder="请选择"
+            @change="search"
+          >
+            <el-option
+              v-for="item in companyOptions"
+              :key="item.id"
+              :label="item.aname"
+              :value="item.id"
+            >
+            </el-option>
+          </el-select>
+          <div class="search-input">
+            <el-date-picker
+              size="mini"
+              v-model="searchYear"
+              type="year"
+              value-format="YYYY"
+              placeholder="选择年"
+              popper-class="date-select"
+            >
+            </el-date-picker>
+          </div>
+        </div>
+        <div class="search-btns">
+          <el-button round size="mini" class="searchColor" @click="search"
+            >搜索</el-button
+          >
+        </div>
+      </div>
+    </div>
+    <div class="data-bodys">
+      <div class="line clearfix">
+        <div class="leftContent left">
+          <span>利用小时分析</span>
+        </div>
+        <div class="rightContent line-right"></div>
+      </div>
+      <div class="economicTable">
+        <el-table
+          :data="tableData"
+          style="width: 100%"
+          size="mini"
+          stripe
+          height="50vh"
+          :header-cell-style="{ 'text-align': 'center' }"
+          @row-click="rowClick"
+        >
+          <el-table-column
+            align="left"
+            prop=""
+            width="80"
+            label="场站"
+            show-overflow-tooltip
+            sortable
+          >
+            <template v-slot="scope">
+              <span style="cursor: pointer">{{ scope.row.wpname }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column
+            align="center"
+            :label="val"
+            v-for="(val, index) in tableHead"
+            :key="index"
+            width="auto"
+            sortable
+          >
+            <template v-slot="scope">
+              <div v-if="scope.row.wpname == ''">
+                <el-row>
+                  <el-col :span="12"
+                    ><span>{{ scope.row.data?.current }}</span></el-col
+                  >
+                  <el-col :span="12"
+                    ><span>{{ scope.row.data.sameperiod }}</span></el-col
+                  >
+                </el-row>
+              </div>
+              <div v-else>
+                <el-row>
+                  <el-col :span="12">
+                    <div style="white-space: nowrap; height: 100%">
+                      <span style="white-space: nowrap">{{
+                        scope.row.targetList &&
+                        (scope.row.targetList[index]?.current ||
+                        scope.row.targetList[index]?.current === 0
+                          ? scope.row.targetList[index]?.current
+                          : "--")
+                      }}</span>
+                      <i
+                        v-if="
+                          scope.row.targetList &&
+                          (scope.row.targetList[index]?.compare > 0 ||
+                            scope.row.targetList[index]?.compare === 0)
+                        "
+                        class="active_icon icon-arrow-up compare-lift-up"
+                      ></i>
+                      <i
+                        v-if="
+                          scope.row.targetList &&
+                          scope.row.targetList[index]?.compare < 0
+                        "
+                        class="active_icon icon-arrow-down compare-lift-down"
+                      ></i>
+                    </div>
+                  </el-col>
+                  <el-col :span="12"
+                    ><span>{{
+                      scope.row.targetList &&
+                      (scope.row.targetList[index]?.sameperiod ||
+                      scope.row.targetList[index]?.sameperiod === 0
+                        ? scope.row.targetList[index]?.sameperiod
+                        : "--")
+                    }}</span></el-col
+                  >
+                </el-row>
+              </div>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+    </div>
+    <div class="echarts" style="height: 30vh">
+      <div class="chart-name">
+        <div class="point left bottom"></div>
+        <div class="point right bottom"></div>
+        利用小时分析
+      </div>
+      <div>
+        <BarCharts
+          :list="barList"
+          width="97%"
+          height="32vh"
+          :top="60"
+          :pillarName="pillarName"
+          :xdate="false"
+          :showLegend="true"
+          :units="['', '']"
+        ></BarCharts>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { GetOrganization } from "@/api/headerNav.js";
+import BarCharts from "../../homePage/components/barCharts.vue";
+import { lyxsList } from "@/api/curveAnalyse";
+export default {
+  name: "utillHours", //利用小时分析
+  components: {
+    // ChartColumnar,
+    // Panel,
+    BarCharts,
+  },
+  data() {
+    return {
+      pillarName: "",
+
+      list: [],
+
+      tabIndex: -2,
+      tabOptions: [
+        { id: -1, name: "风电" },
+        { id: -2, name: "光伏" },
+      ],
+      // barHeight: '260px',
+      barList: [],
+      tableData: [],
+      tableHead: [
+        "一月",
+        "二月",
+        "三月",
+        "四月",
+        "五月",
+        "六月",
+        "七月",
+        "八月",
+        "九月",
+        "十月",
+        "十一月",
+        "十二月",
+      ],
+      company: "",
+      companyOptions: [],
+      searchYear: this.getmonthValue(),
+      selectValue: "利用小时分析",
+    };
+  },
+  watch: {},
+  filters: {},
+  computed: {},
+  created() {
+    this.initialization();
+  },
+  methods: {
+    initialization() {
+      GetOrganization({ type: this.tabIndex }).then((res) => {
+        if (res.data) {
+          this.companyOptions = res.data;
+          this.company = res.data[0].id;
+          this.search();
+        }
+      });
+    },
+    compare(property) {
+      return (a, b) => {
+        let val1 = a[property];
+        let val2 = b[property];
+        return val1 - val2;
+      };
+    },
+    rowClick(list) {
+      if (list?.wpname) {
+        let barList = [
+          {
+            name: "本期",
+            children: [],
+            date: [],
+          },
+          {
+            name: "同期",
+            children: [],
+          },
+        ];
+        this.pillarName = list.wpname;
+        list.targetList.forEach((item) => {
+          barList[0].date.push(item.month + "月");
+          barList[0].children.push(item.current || 0);
+          barList[1].children.push(item.compare || 0);
+        });
+        this.barList = barList;
+      }
+    },
+    isNumber(val) {
+      return typeof val === "number" && !isNaN(val);
+    },
+    search() {
+      lyxsList({
+        companys: this.company,
+        type: this.tabIndex,
+        year: this.monthChange(this.searchYear),
+      }).then((res) => {
+        if (res.data.length) {
+          let arr = [
+            {
+              wpname: "",
+              data: {
+                wpname: "期次",
+                current: "本期",
+                sameperiod: "同期",
+              },
+            },
+          ];
+          this.tableData = [...arr, ...res.data];
+          this.rowClick(res.data[res.data.length - 1]);
+        } else {
+          this.barList = [];
+        }
+      });
+    },
+    monthChange(data) {
+      let year = new Date(data).getFullYear().toString();
+      return year;
+    },
+    getmonthValue() {
+      let year = new Date().getFullYear().toString();
+      return year;
+    },
+
+    tabClick(data) {
+      this.$router.push({
+        path: "/economicsOperation/thematicAnalysis/utillHoursFd",
+      });
+    },
+  },
+  mounted() {},
+  beforeUnmount() {},
+};
+</script>
+
+<style lang="less" scoped>
+.data-bodys {
+  display: flex;
+  flex-direction: column;
+  background-color: rgba(0, 0, 0, 0.45);
+  border-radius: 5px;
+  padding: 5px;
+  .icon-arrow-down:before {
+    color: #ff8300;
+  }
+  .icon-arrow-up:before {
+    color: #1c99ff;
+  }
+}
+
+.parcel-box {
+  padding: 0 20px;
+  box-sizing: border-box;
+
+  .failure-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+  }
+  .search {
+    display: flex;
+    flex-direction: row;
+    padding-top: 10px;
+    padding-bottom: 10px;
+    align-items: center;
+    // justify-content: space-between;
+
+    button {
+      margin-left: 10px;
+      background: rgba(67, 81, 107, 0.3);
+      border: 1px solid #354460;
+      color: #b3b3b3;
+    }
+    ::v-deep .search-left {
+      display: flex;
+      align-items: center;
+      .el-select {
+        margin-right: 10px;
+        width: 155px;
+        .el-input .el-input__inner {
+          width: 150px;
+        }
+      }
+    }
+    .search-btns {
+      display: flex;
+      .searchColor {
+        background-color: rgba(0, 70, 199, 0.2);
+        border: 1px solid #3b4c6c;
+        color: #b3b3b3;
+        font-size: 14px;
+
+        &:hover {
+          background-color: rgba(0, 70, 199, 0.5);
+          color: #ffffff;
+        }
+      }
+    }
+  }
+  .search-right {
+    position: relative;
+    .select-back {
+      position: absolute;
+      top: -1px;
+      left: 0;
+    }
+  }
+
+  .line {
+    padding-bottom: 5px;
+    .leftContent {
+      width: 242px;
+      height: 41px;
+      line-height: 41px;
+      background: url("../../../../../assets/imgs/title_left_bg.png");
+
+      span {
+        font-size: 16px;
+        font-family: Microsoft YaHei;
+        font-weight: 400;
+        color: #ffffff;
+        margin-left: 25px;
+      }
+    }
+
+    .rightContent {
+      width: 212px;
+      height: 28px;
+      margin-top: 13px;
+      background: url("../../../../../assets/imgs/title_right_bg.png");
+    }
+    .left {
+      float: left;
+    }
+    .line-right {
+      float: right;
+    }
+  }
+
+  .echarts {
+    margin-top: 20px;
+    background-color: rgba(0, 0, 0, 0.45);
+    border-radius: 5px;
+    width: 100%;
+  }
+}
+
+.clearfix::after {
+  content: "";
+  clear: both;
+  height: 0;
+  line-height: 0;
+  visibility: hidden;
+  display: block;
+}
+
+.clearfix {
+  zoom: 1;
+}
+
+/deep/ .el-input__prefix {
+  right: -15px;
+  left: unset;
+}
+
+/deep/ .el-input--prefix .el-input__inner {
+  padding-left: 20px;
+}
+
+.el-table::before {
+  height: 0;
+}
+
+.chart-name {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  position: relative;
+  width: 100%;
+  height: 40px;
+  border-bottom: 1px solid rgba(106, 106, 106, 0.5);
+  font-size: 16px;
+  font-family: Microsoft YaHei;
+  font-weight: 400;
+  color: #ffffff;
+}
+
+.point {
+  width: 6px;
+  height: 1px;
+  background-color: #ffffff;
+  position: absolute;
+
+  &.left {
+    left: 0;
+  }
+
+  &.right {
+    right: 0;
+  }
+
+  &.top {
+    top: -1px;
+  }
+
+  &.bottom {
+    bottom: -1px;
+  }
+}
+</style>

+ 478 - 0
src/views/layout/economicsOperation/thematicAnalysis/utillHours/indexFd.vue

@@ -0,0 +1,478 @@
+<template>
+  <div class="parcel-box">
+    <div class="failure-header">
+      <div class="search">
+        <div class="search-left">
+          <el-select
+            size="mini"
+            v-model="tabIndex"
+            placeholder="请选择"
+            @change="tabClick"
+          >
+            <el-option
+              v-for="item in tabOptions"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            >
+            </el-option>
+          </el-select>
+          <el-select
+            size="mini"
+            v-model="company"
+            placeholder="请选择"
+            @change="search"
+          >
+            <el-option
+              v-for="item in companyOptions"
+              :key="item.id"
+              :label="item.aname"
+              :value="item.id"
+            >
+            </el-option>
+          </el-select>
+          <div class="search-input">
+            <el-date-picker
+              size="mini"
+              v-model="searchYear"
+              type="year"
+              value-format="YYYY"
+              placeholder="选择年"
+              popper-class="date-select"
+            >
+            </el-date-picker>
+          </div>
+        </div>
+        <div class="search-btns">
+          <el-button round size="mini" class="searchColor" @click="search"
+            >搜索</el-button
+          >
+        </div>
+      </div>
+    </div>
+    <div class="data-bodys">
+      <div class="line clearfix">
+        <div class="leftContent left">
+          <span>利用小时分析</span>
+        </div>
+        <div class="rightContent line-right"></div>
+      </div>
+      <div class="economicTable">
+        <el-table
+          :data="tableData"
+          style="width: 100%"
+          size="mini"
+          stripe
+          height="50vh"
+          :header-cell-style="{ 'text-align': 'center' }"
+          @row-click="rowClick"
+        >
+          <el-table-column
+            align="left"
+            prop=""
+            width="80"
+            label="场站"
+            show-overflow-tooltip
+            sortable
+          >
+            <template v-slot="scope">
+              <span style="cursor: pointer">{{ scope.row.wpname }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column
+            align="center"
+            :label="val"
+            v-for="(val, index) in tableHead"
+            :key="index"
+            width="auto"
+            sortable
+          >
+            <template v-slot="scope">
+              <div v-if="scope.row.wpname == ''">
+                <el-row>
+                  <el-col :span="12"
+                    ><span>{{ scope.row.data?.current }}</span></el-col
+                  >
+                  <el-col :span="12"
+                    ><span>{{ scope.row.data.sameperiod }}</span></el-col
+                  >
+                </el-row>
+              </div>
+              <div v-else>
+                <el-row>
+                  <el-col :span="12">
+                    <div style="white-space: nowrap; height: 100%">
+                      <span style="white-space: nowrap">{{
+                        scope.row.targetList &&
+                        (scope.row.targetList[index]?.current ||
+                        scope.row.targetList[index]?.current === 0
+                          ? scope.row.targetList[index]?.current
+                          : "--")
+                      }}</span>
+                      <i
+                        v-if="
+                          scope.row.targetList &&
+                          (scope.row.targetList[index]?.compare > 0 ||
+                            scope.row.targetList[index]?.compare === 0)
+                        "
+                        class="active_icon icon-arrow-up compare-lift-up"
+                      ></i>
+                      <i
+                        v-if="
+                          scope.row.targetList &&
+                          scope.row.targetList[index]?.compare < 0
+                        "
+                        class="active_icon icon-arrow-down compare-lift-down"
+                      ></i>
+                    </div>
+                  </el-col>
+                  <el-col :span="12"
+                    ><span>{{
+                      scope.row.targetList &&
+                      (scope.row.targetList[index]?.sameperiod ||
+                      scope.row.targetList[index]?.sameperiod === 0
+                        ? scope.row.targetList[index]?.sameperiod
+                        : "--")
+                    }}</span></el-col
+                  >
+                </el-row>
+              </div>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+    </div>
+    <div class="echarts" style="height: 30vh">
+      <div class="chart-name">
+        <div class="point left bottom"></div>
+        <div class="point right bottom"></div>
+        利用小时分析
+      </div>
+      <div>
+        <BarCharts
+          :list="barList"
+          width="97%"
+          height="32vh"
+          :top="60"
+          :pillarName="pillarName"
+          :xdate="false"
+          :showLegend="true"
+          :units="['', '']"
+        ></BarCharts>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { GetOrganization } from "@/api/headerNav.js";
+import BarCharts from "../../homePage/components/barCharts.vue";
+import { lyxsList } from "@/api/curveAnalyse";
+export default {
+  name: "utillHours", //利用小时分析
+  components: {
+    // ChartColumnar,
+    // Panel,
+    BarCharts,
+  },
+  data() {
+    return {
+      pillarName: "",
+
+      list: [],
+
+      tabIndex: -1,
+      tabOptions: [
+        { id: -1, name: "风电" },
+        { id: -2, name: "光伏" },
+      ],
+      // barHeight: '260px',
+      barList: [],
+      tableData: [],
+      tableHead: [
+        "一月",
+        "二月",
+        "三月",
+        "四月",
+        "五月",
+        "六月",
+        "七月",
+        "八月",
+        "九月",
+        "十月",
+        "十一月",
+        "十二月",
+      ],
+      company: "",
+      companyOptions: [],
+      searchYear: this.getmonthValue(),
+      selectValue: "利用小时分析",
+    };
+  },
+  watch: {},
+  filters: {},
+  computed: {},
+  created() {
+    this.initialization();
+  },
+  methods: {
+    initialization() {
+      GetOrganization({ type: this.tabIndex }).then((res) => {
+        if (res.data) {
+          this.companyOptions = res.data;
+          this.company = res.data[0].id;
+          this.search();
+        }
+      });
+    },
+    compare(property) {
+      return (a, b) => {
+        let val1 = a[property];
+        let val2 = b[property];
+        return val1 - val2;
+      };
+    },
+    rowClick(list) {
+      if (list?.wpname) {
+        let barList = [
+          {
+            name: "本期",
+            children: [],
+            date: [],
+          },
+          {
+            name: "同期",
+            children: [],
+          },
+        ];
+        this.pillarName = list.wpname;
+        list.targetList.forEach((item) => {
+          barList[0].date.push(item.month + "月");
+          barList[0].children.push(item.current || 0);
+          barList[1].children.push(item.compare || 0);
+        });
+        this.barList = barList;
+      }
+    },
+    isNumber(val) {
+      return typeof val === "number" && !isNaN(val);
+    },
+    search() {
+      lyxsList({
+        companys: this.company,
+        type: this.tabIndex,
+        year: this.monthChange(this.searchYear),
+      }).then((res) => {
+        if (res.data.length) {
+          let arr = [
+            {
+              wpname: "",
+              data: {
+                wpname: "期次",
+                current: "本期",
+                sameperiod: "同期",
+              },
+            },
+          ];
+          this.tableData = [...arr, ...res.data];
+          this.rowClick(res.data[res.data.length - 1]);
+        } else {
+          this.barList = [];
+        }
+      });
+    },
+    monthChange(data) {
+      let year = new Date(data).getFullYear().toString();
+      return year;
+    },
+    getmonthValue() {
+      let year = new Date().getFullYear().toString();
+      return year;
+    },
+
+    tabClick(data) {
+      this.$router.push({
+        path: "/economicsOperation/thematicAnalysis/utillHours",
+      });
+    },
+  },
+  mounted() {},
+  beforeUnmount() {},
+};
+</script>
+
+<style lang="less" scoped>
+.data-bodys {
+  display: flex;
+  flex-direction: column;
+  background-color: rgba(0, 0, 0, 0.45);
+  border-radius: 5px;
+  padding: 5px;
+  .icon-arrow-down:before {
+    color: #ff8300;
+  }
+  .icon-arrow-up:before {
+    color: #1c99ff;
+  }
+}
+
+.parcel-box {
+  padding: 0 20px;
+  box-sizing: border-box;
+
+  .failure-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+  }
+  .search {
+    display: flex;
+    flex-direction: row;
+    padding-top: 10px;
+    padding-bottom: 10px;
+    align-items: center;
+    // justify-content: space-between;
+
+    button {
+      margin-left: 10px;
+      background: rgba(67, 81, 107, 0.3);
+      border: 1px solid #354460;
+      color: #b3b3b3;
+    }
+    ::v-deep .search-left {
+      display: flex;
+      align-items: center;
+      .el-select {
+        margin-right: 10px;
+        width: 155px;
+        .el-input .el-input__inner {
+          width: 150px;
+        }
+      }
+    }
+    .search-btns {
+      display: flex;
+      .searchColor {
+        background-color: rgba(0, 70, 199, 0.2);
+        border: 1px solid #3b4c6c;
+        color: #b3b3b3;
+        font-size: 14px;
+
+        &:hover {
+          background-color: rgba(0, 70, 199, 0.5);
+          color: #ffffff;
+        }
+      }
+    }
+  }
+  .search-right {
+    position: relative;
+    .select-back {
+      position: absolute;
+      top: -1px;
+      left: 0;
+    }
+  }
+
+  .line {
+    padding-bottom: 5px;
+    .leftContent {
+      width: 242px;
+      height: 41px;
+      line-height: 41px;
+      background: url("../../../../../assets/imgs/title_left_bg.png");
+
+      span {
+        font-size: 16px;
+        font-family: Microsoft YaHei;
+        font-weight: 400;
+        color: #ffffff;
+        margin-left: 25px;
+      }
+    }
+
+    .rightContent {
+      width: 212px;
+      height: 28px;
+      margin-top: 13px;
+      background: url("../../../../../assets/imgs/title_right_bg.png");
+    }
+    .left {
+      float: left;
+    }
+    .line-right {
+      float: right;
+    }
+  }
+
+  .echarts {
+    margin-top: 20px;
+    background-color: rgba(0, 0, 0, 0.45);
+    border-radius: 5px;
+    width: 100%;
+  }
+}
+
+.clearfix::after {
+  content: "";
+  clear: both;
+  height: 0;
+  line-height: 0;
+  visibility: hidden;
+  display: block;
+}
+
+.clearfix {
+  zoom: 1;
+}
+
+/deep/ .el-input__prefix {
+  right: -15px;
+  left: unset;
+}
+
+/deep/ .el-input--prefix .el-input__inner {
+  padding-left: 20px;
+}
+
+.el-table::before {
+  height: 0;
+}
+
+.chart-name {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  position: relative;
+  width: 100%;
+  height: 40px;
+  border-bottom: 1px solid rgba(106, 106, 106, 0.5);
+  font-size: 16px;
+  font-family: Microsoft YaHei;
+  font-weight: 400;
+  color: #ffffff;
+}
+
+.point {
+  width: 6px;
+  height: 1px;
+  background-color: #ffffff;
+  position: absolute;
+
+  &.left {
+    left: 0;
+  }
+
+  &.right {
+    right: 0;
+  }
+
+  &.top {
+    top: -1px;
+  }
+
+  &.bottom {
+    bottom: -1px;
+  }
+}
+</style>