Kaynağa Gözat

发电能力分析--损失电量分析页面样式及功能完成

baiyanting 1 yıl önce
ebeveyn
işleme
00d47e35af

+ 11 - 0
src/api/powerGenerating/index.js

@@ -320,3 +320,14 @@ export function getWindtempRatedPower(data, timeout = 15000) {
     timeout,
   });
 }
+
+export function getWindLosePower(data, timeout = 15000) {
+  return request({
+    baseURL: process.env.VUE_APP_TEST,
+    url: "/fjjxb/five/loss/cal",
+    method: "get",
+    params: data,
+    headers,
+    timeout,
+  });
+}

+ 11 - 0
src/router/index.js

@@ -1736,6 +1736,17 @@ export const asyncRoutes = [
               permissions: ["jn_dlbb_dmb"],
             },
           },
+          {
+            path: "loseAnalysis",
+            name: "loseAnalysis",
+            component: () =>
+              import("@/views/powerGenerating/windAnalyse/loseAnalysis"),
+            meta: {
+              title: "损失电量分析",
+              icon: "",
+              permissions: ["jn_dlbb_dmb"],
+            },
+          },
         ],
       },
     ],

+ 83 - 63
src/utills/downXlsx.js

@@ -1,70 +1,90 @@
-import  * as XLSX from 'xlsx'
-import { saveAs } from 'file-saver'
-import  * as XLSXD from 'xlsx-js-style'
+import * as XLSX from "xlsx";
+import { saveAs } from "file-saver";
+import * as XLSXD from "xlsx-js-style";
+// 导出的方法  tHeader => 设置Excel的表格第一行的标题  filterVal => 对象的属性key值 被导出listData => 导出数据  => 导出(文件)名称
+import { export_json_to_excel } from "@/tools/excel/Export2Excel"; //引入文件
+
+export function outExportExcel(
+  tHeader = [],
+  filterVal = [],
+  listData = [],
+  exportName = new Date().getTime()
+) {
+  // 注意这个Export2Excel路径
+  const data = formatJson(filterVal, listData);
+  export_json_to_excel(tHeader, data, exportName);
+}
+function formatJson(filterVal, jsonData) {
+  return jsonData.map((v) => filterVal.map((j) => v[j]));
+}
 
 const exportExcel = (el, header, title) => {
-    let $e = el
-    try {
-        let $table = $e.querySelector('.el-table__fixed')
-        if(!$table) {
-            $table = $e
-        }
-        const wb = XLSX.utils.table_to_book($table, {raw:true})
-        if (wb && wb.Sheets.Sheet1) {
-            let as = ['!cols', '!fullref', '!merges', '!ref', '!rows']
-            let bs = header
-            for(let i in wb.Sheets.Sheet1) {
-                if (as.indexOf(i)<0) {
-                    if (bs.indexOf(wb.Sheets.Sheet1[i].v)>=0) {
-                        wb.Sheets.Sheet1[i].s = {
-                            font:{
-                                name: '微软雅黑',
-                                sz: 11,
-                                bold: true,
-                                color: {rgb: 'ffffff'}
-                            },
-                            alignment: {
-                                horizontal: 'center',
-                                vertical: 'center'
-                            },
-                            fill:{fgColor: {rgb: '4f81bd'}}
-                        }
-                        if (i === 'A1') {
-                            wb.Sheets.Sheet1['!cols'].push({wch: 30})
-                        } else if (i === 'B1') {
-                            wb.Sheets.Sheet1['!cols'].push({wch: 20})
-                        } else {
-                            wb.Sheets.Sheet1['!cols'].push({wch: 15})
-                        }
-                    } else {
-                        wb.Sheets.Sheet1[i].s = {
-                            font:{
-                                name: '微软雅黑',
-                                sz: 10,
-                                bold: false,
-                                color: {rgb: '000000'}
-                            },
-                            alignment: {
-                                horizontal: 'center',
-                                vertical: 'center'
-                            },
-                            // fill:{fgColor: {rgb: '4f81bd'}}
-                        }
-                    }
-                }
+  let $e = el;
+  try {
+    let $table = $e.querySelector(".el-table__fixed");
+    if (!$table) {
+      $table = $e;
+    }
+    const wb = XLSX.utils.table_to_book($table, { raw: true });
+    if (wb && wb.Sheets.Sheet1) {
+      let as = ["!cols", "!fullref", "!merges", "!ref", "!rows"];
+      let bs = header;
+      for (let i in wb.Sheets.Sheet1) {
+        if (as.indexOf(i) < 0) {
+          if (bs.indexOf(wb.Sheets.Sheet1[i].v) >= 0) {
+            wb.Sheets.Sheet1[i].s = {
+              font: {
+                name: "微软雅黑",
+                sz: 11,
+                bold: true,
+                color: { rgb: "ffffff" },
+              },
+              alignment: {
+                horizontal: "center",
+                vertical: "center",
+              },
+              fill: { fgColor: { rgb: "4f81bd" } },
+            };
+            if (i === "A1") {
+              wb.Sheets.Sheet1["!cols"].push({ wch: 30 });
+            } else if (i === "B1") {
+              wb.Sheets.Sheet1["!cols"].push({ wch: 20 });
+            } else {
+              wb.Sheets.Sheet1["!cols"].push({ wch: 15 });
             }
+          } else {
+            wb.Sheets.Sheet1[i].s = {
+              font: {
+                name: "微软雅黑",
+                sz: 10,
+                bold: false,
+                color: { rgb: "000000" },
+              },
+              alignment: {
+                horizontal: "center",
+                vertical: "center",
+              },
+              // fill:{fgColor: {rgb: '4f81bd'}}
+            };
+          }
         }
-
-        const wbout = XLSXD.write(wb, {bookType: 'xlsx', bookSST:true, type: 'array'})
-        saveAs(
-            new Blob([wbout],{type: 'application/octet-stream'}),
-            `${title}.xlsx`,
-        )
-    } catch (e) {
-        if (typeof console !== 'undefined') console.error(e)
+      }
     }
-}
+
+    const wbout = XLSXD.write(wb, {
+      bookType: "xlsx",
+      bookSST: true,
+      type: "array",
+    });
+    saveAs(
+      new Blob([wbout], { type: "application/octet-stream" }),
+      `${title}.xlsx`
+    );
+  } catch (e) {
+    if (typeof console !== "undefined") console.error(e);
+  }
+};
 
 export default {
-    exportExcel
-}
+  exportExcel,
+};

+ 1 - 2
src/views/powerGenerating/dataFilter/components/table.vue

@@ -37,7 +37,6 @@ const tableRef = ref("");
   >
     <div class="table-wrapper-title">
       <h3>{{ props.tableName }}</h3>
-      <!-- <el-button size="small" type="primary" @click="funExport" :disabled="!props.tableId">数据导出</el-button> -->
     </div>
     <el-table
       :data="props.data"
@@ -46,7 +45,7 @@ const tableRef = ref("");
       v-loading="props.loading"
       element-loading-background="rgba(22, 31, 30, 0.8)"
       height="calc(100% - 52px)"
-      :style="{ width: '100%' }"
+      style="width: 100%"
     >
       <el-table-column
         align="center"

+ 1 - 1
src/views/powerGenerating/dataFilter/lightPrepare/index.vue

@@ -2,7 +2,7 @@
 import searchCop from "./components/search.vue";
 import excelCop from "@/components/excel.vue";
 import treeCop from "@/components/tree.vue";
-import tableCop from "@/views/powerGenerating/dataFilter/components/table.vue";
+import tableCop from "@/views/powerGenerating/components/table.vue";
 import { ElMessage } from "element-plus";
 import { onMounted, ref, onActivated } from "vue";
 import {

+ 1 - 1
src/views/powerGenerating/dataFilter/prepare/index.vue

@@ -45,7 +45,7 @@ import {
 import searchCop from "./components/search.vue";
 import excelCop from "@/components/excel.vue";
 import treeCop from "@/components/tree.vue";
-import tableCop from "@/views/powerGenerating/dataFilter/components/table.vue";
+import tableCop from "@/views/powerGenerating/components/table.vue";
 import { ElMessage } from "element-plus";
 import { onMounted, ref, onActivated } from "vue";
 const baseURL = process.env.VUE_APP_TEST;

+ 1 - 1
src/views/powerGenerating/dataFilter/process/index.vue

@@ -46,7 +46,7 @@
 import searchCop from "./components/search.vue";
 import excelCop from "@/components/excel.vue";
 import treeCop from "@/components/tree.vue";
-import tableCop from "@/views/powerGenerating/dataFilter/components/table.vue";
+import tableCop from "@/views/powerGenerating/components/table.vue";
 import { ref, onActivated, onMounted } from "vue";
 import request from "@/api/axios.js";
 import { ElMessage } from "element-plus";

+ 12 - 0
src/views/powerGenerating/index.vue

@@ -134,4 +134,16 @@ export default {
     display: block !important;
   }
 }
+.buttons {
+  max-width: 120px;
+  background-color: rgba(5, 187, 76, 0.2);
+  border: 1px solid #3b6c53;
+  color: #b3b3b3;
+  font-size: 14px;
+
+  &:hover {
+    background-color: rgba(5, 187, 76, 0.5);
+    color: #ffffff;
+  }
+}
 </style>

+ 1 - 1
src/views/powerGenerating/windAnalyse/combine/index.vue

@@ -2,7 +2,7 @@
 import searchCop from "./components/search.vue";
 import excelCop from "@/components/excel.vue";
 import treeCop from "@/components/tree.vue";
-import tableCop from "@/views/powerGenerating/dataFilter/components/table.vue";
+import tableCop from "@/views/powerGenerating/components/table.vue";
 import {
   getWindFittingExcelShow,
   getWindFittingData,

+ 1 - 5
src/views/powerGenerating/windAnalyse/hotAnalysis/index.vue

@@ -954,11 +954,7 @@ onActivated(() => {
           <el-tabs v-model="activeTab" class="data-table-tabs">
             <el-tab-pane label="温度与功率" name="1"> </el-tab-pane>
             <el-tab-pane label="温度曲线" name="2"> </el-tab-pane>
-            <div
-              v-show="activeTab === '1'"
-              :style="{ height: tableHeight }"
-              class="data-chart-wrapper"
-            >
+            <div v-show="activeTab === '1'" class="data-chart-wrapper">
               <div class="data-chart-item card-shadow">
                 <el-icon
                   class="zoom-icon"

+ 385 - 0
src/views/powerGenerating/windAnalyse/loseAnalysis/components/barLineChart.vue

@@ -0,0 +1,385 @@
+<template>
+  <div class="chart" :id="id"></div>
+</template>
+
+<script>
+import util from "@tools/util";
+import partten from "@/helper/partten.js";
+import * as echarts from "echarts";
+
+export default {
+  name: "bar-line-chart",
+  componentName: "bar-line-chart",
+  props: {
+    width: {
+      type: String,
+      default: "100%",
+    },
+    height: {
+      type: String,
+      default: "800px",
+    },
+    // 传入数据
+    bardata: {
+      type: Object,
+      default: () => {
+        return {
+          area: [
+            "风场1",
+            "风场2",
+            "风场3",
+            "风场4",
+            "风场5",
+            "风场6",
+            "风场7",
+            "风场8",
+            "风场9",
+          ],
+          legend: [
+            "实际电量",
+            "计划检修损失",
+            "非计划检修损失",
+            "限电损失",
+            "受累损失",
+            "性能损失",
+          ],
+          data: [
+            [1320, 1302, 901, 634, 1390, 1330, 1320, 1000, 500],
+            [320, 302, 301, 334, 390, 330, 320, 100, 50],
+            [320, 302, 301, 334, 390, 330, 320, 100, 50],
+            [1320, 1302, 901, 634, 1390, 1330, 1320, 1000, 500],
+            [320, 302, 301, 334, 390, 330, 320, 100, 50],
+            [320, 302, 301, 334, 390, 330, 320, 100, 50],
+            [1320, 1302, 901, 634, 1390, 1330, 1320, 1000, 500],
+            [320, 302, 301, 334, 390, 330, 320, 100, 50],
+          ],
+        };
+      },
+    },
+    lineData: {
+      type: Array,
+      default: () => [200, 350, 400, 500, 600, 700, 800, 900, 1200],
+    },
+    lineName: {
+      type: String,
+      default: "损失电量",
+    },
+    // 单位
+    units: {
+      type: Array,
+      default: () => ["(万KWh)", "(风速)"],
+    },
+    // 显示 legend
+    showLegend: {
+      type: Boolean,
+      default: true,
+    },
+    // 颜色
+    color: {
+      type: Array,
+      default: () => [
+        "#323E6F",
+        "#e17e23",
+        "#ba3237",
+        "#c531c7",
+        "#ffffff",
+        "#EDEB2F",
+      ],
+    },
+    // 每页显示个数
+    pageSize: {
+      type: Number,
+      default: 20,
+    },
+  },
+  data() {
+    return {
+      id: "",
+      chart: null,
+      themeName: "light",
+      areaData: [],
+    };
+  },
+  methods: {
+    initChart() {
+      let chart = echarts.init(this.$el);
+      this.chart = chart;
+      let option = {
+        color: this.color,
+        grid: {
+          left: 40,
+          right: 40,
+          bottom: 16,
+          top: 16,
+          containLabel: true,
+        },
+        legend: {
+          show: this.showLegend,
+          data: this.bardata.legend,
+          right: 56,
+          icon: "ract",
+          itemWidth: 8,
+          itemHeight: 8,
+          inactiveColor:
+            this.themeName === "dark" ? partten.getColor("gray") : "#838383",
+          textStyle: {
+            color:
+              this.themeName === "dark" ? partten.getColor("grayl") : "#838383",
+            fontSize: 12,
+          },
+        },
+        tooltip: {
+          trigger: "axis",
+          backgroundColor: true
+            ? "rgba(255,255,255,0.9)"
+            : "rgba(255,255,255,0.9)",
+          borderColor:
+            this.themeName === "dark" ? partten.getColor("gray") : "#838383",
+          textStyle: {
+            color: this.themeName === "dark" ? "#fff" : "#838383",
+            fontSize: util.vh(16),
+          },
+        },
+        dataZoom: [
+          {
+            type: "inside",
+            start: 0,
+            end: this.end,
+            yAxisIndex: [0],
+          },
+          {
+            start: 0,
+            end: this.end,
+            top: 20,
+            bottom: 40,
+            yAxisIndex: [0],
+            backgroundColor: "transparent",
+            // handleIcon: "path://M306.1,413c0,2.2-1.8,4-4,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z",
+            handleStyle: {
+              color:
+                this.themeName === "dark"
+                  ? partten.getColor("green")
+                  : partten.getColor("blue"),
+            },
+            moveHandleSize: 0,
+            // dataBackground: {
+            //   lineStyle: {
+            //     color: partten.getColor("gray"),
+            //   },
+            //   areaStyle: {
+            //     color: partten.getColor("gray"),
+            //   },
+            // },
+            // selectedDataBackground: {
+            //   lineStyle: {
+            //     color: partten.getColor("yellow"),
+            //   },
+            //   areaStyle: {
+            //     color: partten.getColor("yellow"),
+            //   },
+            // },
+            fillerColor: "transparent",
+            textStyle: {
+              color:
+                this.themeName === "dark"
+                  ? partten.getColor("grayl")
+                  : "#838383",
+            },
+            borderColor:
+              this.themeName === "dark" ? partten.getColor("gray") : "#838383",
+            brushSelect: false,
+          },
+        ],
+        yAxis: [
+          {
+            type: "category",
+            axisLabel: {
+              color:
+                this.themeName === "dark"
+                  ? partten.getColor("gray")
+                  : "#838383",
+            },
+            inverse: true,
+            // minInterval: 10,
+            // maxInterval: 10,
+            axisLine: {
+              show: false,
+            },
+            axisTick: {
+              show: false,
+            },
+            data: this.areaData,
+          },
+        ],
+        xAxis: [
+          {
+            type: "value",
+            name: "万kWh",
+            axisLabel: {
+              color:
+                this.themeName === "dark"
+                  ? partten.getColor("gray")
+                  : "#838383",
+              // formatter: "{value}万kWh",
+            },
+            axisLine: {
+              type: "dashed",
+              lineStyle: {
+                color:
+                  this.themeName === "dark"
+                    ? partten.getColor("gray")
+                    : "#838383",
+              },
+              width: 5,
+            },
+            axisTick: {
+              show: false,
+            },
+            splitLine: {
+              lineStyle: {
+                type: "dashed",
+                dashOffset: 10,
+                color: this.themeName === "dark" ? "#5a6162" : "#838383" + 80,
+              },
+            },
+          },
+          {
+            type: "value",
+            name: "",
+            axisLabel: {
+              show: false,
+              // formatter: "{value}万kWh",
+              // color: partten.getColor("gray"),
+            },
+            axisLine: {
+              show: false,
+            },
+            axisTick: {
+              show: false,
+            },
+            splitLine: {
+              show: false,
+            },
+          },
+        ],
+        series: [],
+      };
+
+      if (this.bardata && this.bardata.legend)
+        // bar data
+        for (var i = 0; i < this.bardata.legend.length; i++) {
+          option.series.push({
+            name: this.bardata.legend[i],
+            type: "bar",
+            stack: "总量",
+            barWidth: 16,
+            label: {
+              show: false,
+              position: "insideRight",
+            },
+            data: this.bardata.data[i],
+          });
+        }
+
+      // line data
+      if (this.lineData.length > 0) {
+        option.series.push({
+          name: this.lineName,
+          type: "line",
+          data: this.lineData,
+          smooth: false, //平滑展示
+          xAxisIndex: 1,
+          lineStyle: {
+            color:
+              this.themeName === "dark"
+                ? partten.getColor("green")
+                : partten.getColor("blue"),
+          },
+          itemStyle: {
+            color:
+              this.themeName === "dark"
+                ? partten.getColor("green")
+                : partten.getColor("blue"),
+          },
+        });
+      }
+      chart.setOption(option);
+      chart.resize();
+    },
+  },
+  created() {
+    this.id = "pie-chart-" + util.newGUID();
+    if (this.bardata.area && this.bardata.area.length < this.pageSize) {
+      this.areaData = this.bardata.area;
+      for (let i = this.bardata.area.length; i <= this.pageSize; i++) {
+        this.areaData.push("");
+      }
+    }
+  },
+  mounted() {
+    this.$nextTick(() => {
+      this.$el.style.width = this.width;
+      this.$el.style.height = this.height;
+      this.initChart();
+    });
+  },
+  updated() {
+    this.$nextTick(() => {
+      this.initChart();
+    });
+  },
+  beforeUpdate() {
+    this.areaData = this.bardata.area;
+  },
+  beforeUpdate() {
+    this.areaData = this.bardata.area;
+  },
+  activated() {
+    this.$nextTick(() => {
+      this.$el.style.width = this.width;
+      this.$el.style.height = this.height;
+      this.initChart();
+    });
+  },
+  computed: {
+    collapse() {
+      return this.$store.state.collapse;
+    },
+    legend() {
+      return this.bardata.legend;
+    },
+    end() {
+      var result = 20;
+      if (this.areaData) {
+        result = parseInt((this.pageSize / this.areaData.length) * 100);
+      }
+      return result;
+    },
+  },
+  watch: {
+    collapse(val) {
+      if (this.chart) {
+        setTimeout(() => {
+          this.chart.resize();
+        }, 300);
+      }
+    },
+    bardata(val) {
+      if (val.area && val.area.length < this.pageSize) {
+        this.areaData = val.area;
+        for (let i = val.area.length; i <= this.pageSize; i++) {
+          this.areaData.push("");
+        }
+      }
+    },
+  },
+};
+</script>
+
+<style scoped>
+.chart {
+  width: 100%;
+  height: 100%;
+  display: inline-block;
+}
+</style>

+ 119 - 0
src/views/powerGenerating/windAnalyse/loseAnalysis/components/search.vue

@@ -0,0 +1,119 @@
+<script setup name="search">
+import { onMounted, reactive, ref } from 'vue'
+import request from '@/api/axios.js'
+import SubmitBtn from '@com/SubmitBtn.vue'
+
+const queryForm = reactive({
+	station: '',
+	lineIds: [],
+	wtIds: [],
+	type: '1',
+	st: Date.now() - 30 * 24 * 60 * 60 * 1000,
+	et: Date.now(),
+})
+/**场站 */
+const stationList = ref([])
+const funGetStation = async () => {
+	const res = await request.get("/base/station")
+	stationList.value = res.data
+}
+const funStationChange = (stationId) => {
+	if (stationId) {
+		funGetWind(stationId)
+	} else {
+		queryForm.wtIds = []
+		windList.value = []
+	}
+	funSubmit()
+}
+/**项目 */
+const windList = ref([])
+const funGetWind = async (stationId) => {
+	const res = await request.get("/base/project", {params: { stationId }})
+	windList.value = res.data
+	queryForm.wtIds = []
+}
+const funWindChange = (windArr) => {
+	if (windArr?.length) {
+		funGetLine(windArr)
+	} else {
+		queryForm.lineIds = []
+		lineList.value = []
+	}
+	funSubmit()
+}
+/**期次 */
+const lineList = ref([])
+const funGetLine = async (wtIds) => {
+	const res = await request.get("/base/line", {params: { projectId: wtIds.join() }})
+	lineList.value = res.data
+	queryForm.lineIds = []
+}
+/**导出 */
+const emits = defineEmits(['submit'])
+const funSubmit = async () => {
+	const query = {
+		station: queryForm.station,
+		line: queryForm.lineIds.join(),
+		project: queryForm.wtIds.join(),
+		type: '',
+		st: new Date(queryForm.st).formatDate('yyyy-MM-dd'),
+		et: new Date(queryForm.et).formatDate('yyyy-MM-dd'),
+	}
+	emits('submit', query)
+}
+const funType = (type) => {
+	queryForm.type = type
+	queryForm.station = ''
+	queryForm.lineIds = []
+	queryForm.wtIds = []
+	const query = {
+		station: queryForm.station,
+		line: queryForm.lineIds.join(),
+		project: queryForm.wtIds.join(),
+		type: queryForm.type,
+		st: new Date(queryForm.st).formatDate('yyyy-MM-dd'),
+		et: new Date(queryForm.et).formatDate('yyyy-MM-dd'),
+	}
+	emits('submit',query)
+}
+/**created */
+funGetStation()
+funType('1')
+</script>
+<template>
+	<div class="pl-[20px] flex items-center h-[80px] relative">
+		<div class="absolute top-[-7px] left-[20px] text-[#838383] text-[14px]">操作面板</div>
+		<el-form class="" :inline="true" :model="queryForm">
+			<el-form-item label="场站" class="!mb-0">
+				<el-select v-model="queryForm.station" class="w-[150px]" @change="funStationChange">
+					<el-option v-for="item in stationList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+				</el-select>
+			</el-form-item>
+			<el-form-item label="期次" class="!mb-0">
+				<el-select multiple class="w-[150px]" clearable v-model="queryForm.wtIds" @change="funWindChange" collapse-tags>
+					<el-option v-for="item in windList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+				</el-select>
+			</el-form-item>
+			<el-form-item label="线路" class="!mb-0">
+				<el-select multiple class="w-[150px]" clearable v-model="queryForm.lineIds" @change="funSubmit" collapse-tags>
+					<el-option v-for="item in lineList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+				</el-select>
+			</el-form-item>
+			<el-form-item label="开始时间" class="!mb-0">
+				<el-date-picker type="date" class="!w-[150px]" v-model="queryForm.st" @change="funSubmit" ></el-date-picker>
+			</el-form-item>
+			<el-form-item label="结束时间" class="!mb-0">
+				<el-date-picker type="date" class="!w-[150px]" v-model="queryForm.et" @change="funSubmit" ></el-date-picker>
+			</el-form-item>
+			<el-form-item class="!mb-0">
+				<submit-btn v-prevdbclick:5000="funSubmit" desc="执行"></submit-btn>
+			</el-form-item>
+			<el-form-item class="!mb-0">
+				<submit-btn @click="funType('1')" :type="queryForm.type==='1'? 'success': 'default'" desc="风场"></submit-btn>
+				<submit-btn @click="funType('2')" :type="queryForm.type==='2'? 'success': 'default'" desc="期次"></submit-btn>
+				<submit-btn @click="funType('3')" :type="queryForm.type==='3'? 'success': 'default'" desc="集电线路"></submit-btn>
+			</el-form-item>
+		</el-form>
+	</div>
+</template>

+ 134 - 0
src/views/powerGenerating/windAnalyse/loseAnalysis/components/table.vue

@@ -0,0 +1,134 @@
+<script setup name="table">
+import { outExportExcel } from "@/utills/downXlsx.js";
+import { ref, computed, defineProps, defineEmits } from "vue";
+const props = defineProps({
+  showShadow: { type: Boolean, default: true },
+  data: {
+    type: Array,
+    default: () => [],
+  },
+  column: {
+    type: Array,
+    default: () => [],
+  },
+  tableName: {
+    type: String,
+    default: "",
+  },
+  tableId: {
+    type: String,
+    default: "",
+  },
+  loading: {
+    type: Boolean,
+    default: false,
+  },
+  showSummary: {
+    type: Boolean,
+    default: false,
+  },
+  summaryMethod: {
+    type: Function,
+    default: () => () => {},
+  },
+});
+const emits = defineEmits(["export"]);
+const funExport = () => {
+  const columns = props.column.map((o) => {
+    return {
+      ...o,
+      property: o.prop,
+    };
+  });
+  let summary = [];
+  const summaryObj = {};
+  const tHeader = [];
+  const tHeaderKeys = [];
+  const tableData = [];
+  if (props.showSummary && props.summaryMethod) {
+    summary = props.summaryMethod({ columns, data: props.data });
+  }
+  for (const key in props.column) {
+    tHeader.push(props.column[key].label);
+    tHeaderKeys.push(props.column[key].prop);
+    if (props.showSummary && props.summaryMethod) {
+      summaryObj[props.column[key].prop] = summary[key];
+    }
+  }
+  const exportName = props.tableName;
+  tableData.push(...props.data);
+  if (props.showSummary && props.summaryMethod) {
+    tableData.push(summaryObj);
+  }
+  outExportExcel(tHeader, tHeaderKeys, tableData, exportName);
+};
+const tableRef = ref("");
+</script>
+<template>
+  <div
+    ref="tableRef"
+    class="table-wrapper"
+    :class="showShadow ? 'card-shadow' : ''"
+  >
+    <div class="table-wrapper-title">
+      <h3>{{ props.tableName }}</h3>
+      <el-button
+        size="small"
+        class="buttons"
+        @click="funExport"
+        :disabled="!props.tableId"
+        >数据导出</el-button
+      >
+    </div>
+    <el-table
+      :data="props.data"
+      stripe
+      :show-summary="showSummary"
+      :summary-method="summaryMethod"
+      size="small"
+      height="calc(100% - 52px)"
+      v-loading="props.loading"
+      element-loading-background="rgba(22, 31, 30, 0.8)"
+      style="width: 100%"
+    >
+      <el-table-column
+        align="center"
+        :key="index"
+        show-overflow-tooltip
+        v-for="(item, index) in props.column"
+        :prop="item.prop"
+        :label="item.label"
+        sortable
+        resizable
+        :min-width="item.width ? item.width : 80"
+      />
+    </el-table>
+  </div>
+</template>
+<style lang="less" scoped>
+.table-wrapper {
+  height: 100%;
+  padding: 20px;
+  padding-top: 0;
+  width: calc(62% - 40px);
+}
+.table-wrapper-title {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+.el-table::v-deep {
+  .el-table__footer-wrapper tbody td.el-table__cell {
+    font-size: 14px;
+    background-color: #282f31 !important;
+    color: #fff !important;
+  }
+  .el-table__footer-wrapper tbody tr {
+    &:hover {
+      td.el-table__cell {
+        color: #05bb4c !important;
+      }
+    }
+  }
+}
+</style>

+ 359 - 0
src/views/powerGenerating/windAnalyse/loseAnalysis/index.vue

@@ -0,0 +1,359 @@
+<script setup name="loseAnalysis">
+import searchCop from "./components/search.vue";
+import tableCop from "./components/table.vue";
+import excelCop from "@/components/excel.vue";
+import treeCop from "@/components/tree.vue";
+import {
+  getWindLosePower,
+  getPrepareTree,
+} from "@/api/powerGenerating/index.js";
+import barLineChartCop from "./components/barLineChart.vue";
+import { ElMessage } from "element-plus";
+import { onMounted, ref, onActivated } from "vue";
+
+/**table 开始 */
+const tableShowId = ref("");
+const tableName = ref("损失电量分析");
+const tableColumn = ref([]);
+const tableLoading = ref(false);
+const tableData = ref([]);
+const funSummary = ({ columns, data }) => {
+  const sums = [];
+  columns.forEach((column, index) => {
+    if (index === 0) {
+      sums[index] = "合计";
+      return;
+    }
+    const values = data.map((item) => Number(item[column.property]));
+    if (!values.every((value) => Number.isNaN(value))) {
+      sums[index] = values.reduce((prev, curr) => {
+        const value = Number(curr);
+        if (!Number.isNaN(value)) {
+          return Number((prev + curr).toFixed(2));
+        } else {
+          return Number(prev.toFixed(2));
+        }
+      }, 0);
+    } else {
+      sums[index] = "--";
+    }
+    if (["speed", "fnlly"].includes(column.property)) {
+      if (!Number.isNaN(sums[index])) {
+        sums[index] = Number((sums[index] / data.length).toFixed(2));
+      }
+    }
+  });
+
+  return sums;
+};
+const funSubmit = async () => {
+  if (!excelCheckIds.value.length) {
+    ElMessage.error("请勾选要展现的项");
+    return false;
+  }
+  tableLoading.value = true;
+  tableData.value = [];
+  tableShowId.value = "";
+  barData.value = {
+    area: [],
+    legend: [],
+    data: [],
+  };
+  lineData.value = [];
+  const res = await getWindLosePower({
+    ids: excelCheckIds.value.join(),
+  });
+  if (res.code !== 200 || !res.data.title) {
+    tableLoading.value = false;
+    return false;
+  }
+  ElMessage.success(res.msg);
+  tableColumn.value = res.data.title.map((o) => {
+    return {
+      prop: o.key,
+      label: o.des,
+      width: o.des === "时间" ? 100 : 80,
+    };
+  });
+  tableData.value = res.data.data;
+
+  const name = [],
+    data = [],
+    llfdl = [],
+    legend = [
+      "实际电量",
+      "计划检修损失",
+      "非计划检修损失",
+      "限电损失",
+      "受累损失",
+      "性能损失",
+    ],
+    data2 = []; //项目列表
+  // if (params.station) {
+  // 	let arr = [];
+  // 	let hj = res.data.data.pop();
+  // 	res.data.data.forEach((ele, index) => {
+  // 		arr[ele.id.split('_')[1] - 1] = ele
+  // 	})
+  // 	arr.push(hj);
+  // 	res.data.data = arr;
+  // }
+
+  res.data.data.forEach((item, index) => {
+    name.push(item.name);
+    data.push([item.sjfdl, item.jhjx, item.fjhjx, item.xd, item.sl, item.xn]);
+    llfdl.push(item.llfdl);
+    data2.push({
+      index: index + 1,
+      name: item.name,
+      llfdl: item.llfdl,
+      sjfdl: item.sjfdl,
+      speed: item.speed,
+      fjhjx: item.fjhjx,
+      jhjx: item.jhjx,
+      sl: item.sl,
+      xd: item.xd,
+      xn: item.xn,
+      fnlly: item.fnlly,
+      is_light: false,
+    });
+  });
+  if (data.length > 0) {
+    let arr1 = [];
+    const length = data[0].length;
+    for (var i = 0; i < length; i++) {
+      let arr2 = [];
+      data.forEach((ele) => {
+        arr2.push(ele[i]);
+      });
+      arr1.push(arr2);
+    }
+    lineData.value = llfdl;
+    barData.value = {
+      area: name,
+      legend: legend,
+      data: arr1,
+    };
+  }
+
+  tableLoading.value = false;
+  tableShowId.value = "1";
+  activeTab.value = "2";
+};
+/**barlineChart 开始 */
+const barData = ref({
+  area: [],
+  legend: [],
+  data: [],
+});
+const lineData = ref([]);
+const barColor = [
+  "#4b55ae",
+  "#e17e23",
+  "#ba3237",
+  "#c531c7",
+  "rgb(63,177,227)",
+  "#05bb4c",
+];
+
+/**tabs */
+const activeTab = ref("1");
+/**excel 开始 */
+const excelCheckboxShow = ref(false);
+const excelType = ref("");
+const treeCopRef = ref(); //treeCop ref
+const excelCheckIds = ref([]);
+const excelList = ref([]);
+const funExcelChange = async (obj) => {
+  //点击excel项时
+  return false;
+};
+const funExcelCheckChange = ({ checkArr, data }) => {
+  //bug
+  excelCheckIds.value = checkArr;
+  funSubmit();
+};
+
+/**prepare tree 开始 */
+const treeData = ref([]);
+const actTreeNode = ref(null); //当前激活的treeNode
+const funRepeatMap = (arr) => {
+  return arr.map((o) => {
+    if (o.children) {
+      const findIndex = o.children.findIndex((p) => !!p.type);
+      if (findIndex !== -1) {
+        o.childs = o.children;
+        o.children = [];
+        if (!actTreeNode.value) {
+          //判断当且仅有process获取tree时 赋值
+          actTreeNode.value = o;
+        }
+      }
+    }
+    return {
+      ...o,
+      children: o.children ? funRepeatMap(o.children) : [],
+    };
+  });
+};
+const funGetTree = async () => {
+  actTreeNode.value = null;
+  const res = await getPrepareTree();
+  treeData.value = funRepeatMap(res.data);
+  excelList.value = [];
+  if (actTreeNode.value) {
+    funCurrentChange({ current: actTreeNode.value, currentNode: null });
+    if (treeCopRef.value) {
+      treeCopRef.value.setCheckedKeys([actTreeNode.value.id]);
+      excelCheckIds.value = actTreeNode.value.childs.map((o) => o.id);
+      funSubmit();
+    }
+  }
+};
+const funCurrentChange = ({ current, currentNode }) => {
+  excelCheckboxShow.value = true;
+  if (current.childs) {
+    excelList.value = current.childs.map((o) => {
+      return {
+        id: o.id,
+        interval: o.interval,
+        path: o.path,
+        prepareid: o.prepareid,
+        station: o.station,
+        time: o.time,
+        type: o.type,
+        windturbine: o.windturbine,
+        name: o.path.substring(
+          o.path.indexOf(o.station + "_") + (o.station + "_").length
+        ),
+      };
+    });
+  } else {
+    excelList.value = [];
+  }
+};
+const funTreeCheckChange = ({
+  current,
+  checkedNodes,
+  checkedKeys,
+  halfCheckedNodes,
+  halfCheckedKeys,
+}) => {
+  //tree change  -> excel change
+  funCurrentChange({ current, currentNode: "" });
+  const checkIds = [];
+  if (checkedNodes.length) {
+    for (const node of checkedNodes) {
+      if (node.childs && node.childs.length) {
+        for (const child of node.childs) {
+          checkIds.push(child.id);
+        }
+      }
+    }
+  }
+  excelCheckIds.value = checkIds;
+  funSubmit();
+};
+/**created */
+funGetTree();
+/**activated */
+onActivated(() => {
+  funGetTree();
+});
+</script>
+<template>
+  <div class="container-wrapper">
+    <div class="power-data-wrapper card-shadow wrapper">
+      <div class="card-title">数据展示</div>
+      <div class="data-wrapper">
+        <tree-cop
+          ref="treeCopRef"
+          :data="treeData"
+          @checkChange="funTreeCheckChange"
+          :show-checkbox="true"
+          @currentChange="funCurrentChange"
+          @refresh="funGetTree"
+        />
+        <excel-cop
+          :checkIds="excelCheckIds"
+          :showCheckbox="excelCheckboxShow"
+          :data="excelList"
+          @excelChange="funExcelChange"
+          @checkChange="funExcelCheckChange"
+        />
+        <div class="data-table-wrapper card-shadow">
+          <el-tabs v-model="activeTab">
+            <el-tab-pane label="表格数据" name="1"> </el-tab-pane>
+            <el-tab-pane label="图表展示" name="2"> </el-tab-pane>
+            <table-cop
+              v-show="activeTab === '1'"
+              :data="tableData"
+              :showShadow="false"
+              :showSummary="true"
+              :summaryMethod="funSummary"
+              :column="tableColumn"
+              :loading="tableLoading"
+              :tableId="tableShowId"
+              :tableName="tableName"
+            />
+            <div v-show="activeTab === '2'" class="data-chart-wrapper">
+              <bar-line-chart-cop
+                v-show="lineData.length"
+                width="100%"
+                height="100%"
+                :bardata="barData"
+                :lineData="lineData"
+                :color="barColor"
+                lineName="理论发电量"
+              ></bar-line-chart-cop>
+              <el-empty
+                v-show="!lineData.length"
+                description="请选择条件"
+              ></el-empty>
+            </div>
+          </el-tabs>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<style lang="less" scoped>
+.power-data-wrapper {
+  height: 100%;
+  margin-top: 0;
+}
+.data-table-wrapper {
+  width: calc(62% - 40px) !important;
+}
+.el-tabs ::v-deep {
+  height: 100%;
+  .el-tabs__item {
+    color: #b3b3b3;
+    padding: 0 20px;
+  }
+  .el-tabs__nav-wrap::after {
+    background-color: #14221f;
+  }
+  .el-tabs__active-bar {
+    background-color: #05bb4c;
+  }
+ .el-tabs__item.is-top:last-child {
+    padding: 0;
+    padding-left: 5px;
+  }
+  .el-tabs__item.is-active,
+  .el-tabs__item:hover {
+    color: #05bb4c;
+  }
+  .el-tabs__content {
+    height: calc(100% - 45px);
+    .table-wrapper {
+      width: 100%;
+    }
+    .data-chart-wrapper {
+      width: 100%;
+      height: 100%;
+    }
+  }
+}
+</style>