Jelajahi Sumber

风电功率曲线拟合页面和对风偏差分析页面功能实现和样式调整

baiyanting 1 tahun lalu
induk
melakukan
35fa6a25ae

+ 155 - 2
src/api/powerGenerating/index.js

@@ -3,6 +3,7 @@ import request from "@/tools/request";
 const headers = {
   isPower: true,
 };
+//获取风电场站
 export function getStation(data, timeout = 5000) {
   return request({
     baseURL: process.env.VUE_APP_TEST,
@@ -12,6 +13,17 @@ export function getStation(data, timeout = 5000) {
     timeout,
   });
 }
+//获取光伏电站
+export function getGfstation(data, timeout = 5000) {
+  return request({
+    baseURL: process.env.VUE_APP_TEST,
+    url: "/base/gfstation",
+    method: "get",
+    headers,
+    timeout,
+  });
+}
+// 获取风机设备
 export function getWtByStation(data, timeout = 5000) {
   return request({
     baseURL: process.env.VUE_APP_TEST,
@@ -22,6 +34,18 @@ export function getWtByStation(data, timeout = 5000) {
     timeout,
   });
 }
+// 获取光伏设备
+export function getWtByGfstation(data, timeout = 5000) {
+  return request({
+    baseURL: process.env.VUE_APP_TEST,
+    url: "base/inverter",
+    method: "get",
+    params: data,
+    headers,
+    timeout,
+  });
+}
+// 风电准备树
 export function getPrepareTree(data, timeout = 5000) {
   return request({
     baseURL: process.env.VUE_APP_TEST,
@@ -32,6 +56,7 @@ export function getPrepareTree(data, timeout = 5000) {
     timeout,
   });
 }
+// 风电处理树
 export function getProcessTree(data, timeout = 5000) {
   return request({
     baseURL: process.env.VUE_APP_TEST,
@@ -42,6 +67,19 @@ export function getProcessTree(data, timeout = 5000) {
     timeout,
   });
 }
+// 风电拟合树
+export function getWindFittingTree(data, timeout = 5000) {
+  return request({
+    baseURL: process.env.VUE_APP_TEST,
+    url: "/power/fitting/tree",
+    method: "get",
+
+    headers,
+    timeout,
+  });
+}
+
+// 风电准备表单
 export function getPrepareExcelShow(data, timeout = 5000) {
   return request({
     baseURL: process.env.VUE_APP_TEST,
@@ -52,6 +90,7 @@ export function getPrepareExcelShow(data, timeout = 5000) {
     timeout,
   });
 }
+// 风电处理表单
 export function getProcessExcelShow(data, timeout = 5000) {
   return request({
     baseURL: process.env.VUE_APP_TEST,
@@ -62,6 +101,7 @@ export function getProcessExcelShow(data, timeout = 5000) {
     timeout,
   });
 }
+// 风电准备表格数据
 export function getPrepareData(data, timeout = 5000) {
   return request({
     baseURL: process.env.VUE_APP_TEST,
@@ -72,16 +112,18 @@ export function getPrepareData(data, timeout = 5000) {
     timeout,
   });
 }
+// 风电处理表格数据
 export function getProcessData(data, timeout = 5000) {
   return request({
     baseURL: process.env.VUE_APP_TEST,
-    url: "/power/prepare/data",
+    url: "/power/process/show",
     method: "get",
     params: data,
     headers,
     timeout,
   });
 }
+//
 export function fittingCurveSave(data, timeout = 5000) {
   return request({
     baseURL: process.env.VUE_APP_TEST,
@@ -92,6 +134,7 @@ export function fittingCurveSave(data, timeout = 5000) {
     timeout,
   });
 }
+// 树项删除
 export function dataOptionDel(data, timeout = 5000) {
   return request({
     baseURL: process.env.VUE_APP_TEST,
@@ -105,7 +148,117 @@ export function dataOptionDel(data, timeout = 5000) {
 export function filesDel(data, timeout = 5000) {
   return request({
     baseURL: process.env.VUE_APP_TEST,
-    url: "/data/option/delete",
+    url: "/delete/files",
+    method: "get",
+    params: data,
+    headers,
+    timeout,
+  });
+}
+//光伏准备表单
+export function lightPrepareExcelShow(data, timeout = 5000) {
+  return request({
+    baseURL: process.env.VUE_APP_TEST,
+    url: "/new/photovol/datas",
+    method: "get",
+    params: data,
+    headers,
+    timeout,
+  });
+}
+// 光伏准备树
+export function getLightPrepareTree(data, timeout = 5000) {
+  return request({
+    baseURL: process.env.VUE_APP_TEST,
+    url: "/new/photovol/allfilelist",
+    method: "get",
+
+    headers,
+    timeout,
+  });
+}
+// 光伏准备表格数据
+export function getLightPrepareData(data, timeout = 5000) {
+  return request({
+    baseURL: process.env.VUE_APP_TEST,
+    url: "/new/photovol/filelist",
+    method: "get",
+    params: data,
+    headers,
+    timeout,
+  });
+}
+// 风电拟合表单
+export function getWindFittingExcelShow(data, timeout = 5000) {
+  return request({
+    baseURL: process.env.VUE_APP_TEST,
+    url: "/power/fitting/curve",
+    method: "get",
+    params: data,
+    headers,
+    timeout,
+  });
+}
+// 风电拟合表格
+export function getWindFittingShow(data, timeout = 5000) {
+  return request({
+    baseURL: process.env.VUE_APP_TEST,
+    url: "/power/fitting/show",
+    method: "get",
+    params: data,
+    headers,
+    timeout,
+  });
+}
+
+export function getWindFittingData(data, timeout = 5000) {
+  return request({
+    baseURL: process.env.VUE_APP_TEST,
+    url: "/power/fitting/data",
+    method: "get",
+    params: data,
+    headers,
+    timeout,
+  });
+}
+
+export function getWindFittingFilter(data, timeout = 5000) {
+  return request({
+    baseURL: process.env.VUE_APP_TEST,
+    url: "/power/fitting/filter",
+    method: "get",
+    params: data,
+    headers,
+    timeout,
+  });
+}
+
+export function getWindFittingAlarms(data, timeout = 5000) {
+  return request({
+    baseURL: process.env.VUE_APP_TEST,
+    url: "/power/fitting/alarms",
+    method: "get",
+    params: data,
+    headers,
+    timeout,
+  });
+}
+
+export function getWindRoses(data, timeout = 5000) {
+  return request({
+    baseURL: process.env.VUE_APP_TEST,
+    url: "/wind/roses",
+    method: "get",
+    params: data,
+    headers,
+    timeout,
+  });
+}
+
+export function getWindDeviationRatio(data, timeout = 5000) {
+  return request({
+    baseURL: process.env.VUE_APP_TEST,
+    url: "/wind/deviation/ratio",
     method: "get",
     params: data,
     headers,

+ 1 - 1
src/assets/styles/dialog.less

@@ -540,7 +540,7 @@
   }
 
   .el-select .el-select__tags .el-tag {
-    background-color: rgba(30, 90, 163, 0.5);
+    background-color: #343a39;
     color: #ffffff;
   }
 

+ 1 - 0
src/components/SubmitBtn.vue

@@ -19,6 +19,7 @@ const props = defineProps({
 </template>
 <style lang="less" scoped>
 .buttons {
+  max-width: 120px;
   background-color: rgba(5, 187, 76, 0.2);
   border: 1px solid #3b6c53;
   color: #b3b3b3;

+ 49 - 2
src/router/index.js

@@ -1637,7 +1637,8 @@ export const asyncRoutes = [
           {
             path: "prepare",
             name: "prepare",
-            component: () => import("@/views/powerGenerating/dataFilter/prepare"),
+            component: () =>
+              import("@/views/powerGenerating/dataFilter/prepare"),
             meta: {
               title: "风电数据准备",
               icon: "",
@@ -1647,13 +1648,59 @@ export const asyncRoutes = [
           {
             path: "process",
             name: "process",
-            component: () => import("@/views/powerGenerating/dataFilter/process"),
+            component: () =>
+              import("@/views/powerGenerating/dataFilter/process"),
             meta: {
               title: "风电数据处理",
               icon: "",
               permissions: ["jn_dlbb_dmb"],
             },
           },
+          {
+            path: "lightPrepare",
+            name: "lightPrepare",
+            component: () =>
+              import("@/views/powerGenerating/dataFilter/lightPrepare"),
+            meta: {
+              title: "光伏数据准备",
+              icon: "",
+              permissions: ["jn_dlbb_dmb"],
+            },
+          },
+        ],
+      },
+      {
+        path: "windAnalyse",
+        name: "windAnalyse",
+        meta: {
+          title: "风电分析",
+          icon: "svg-wind-site",
+          permissions: ["jn_dlbb"],
+        },
+        component: () => import("@/views/powerGenerating/windAnalyse"),
+        children: [
+          {
+            path: "combine",
+            name: "combine",
+            component: () =>
+              import("@/views/powerGenerating/windAnalyse/combine"),
+            meta: {
+              title: "功率曲线拟合分析",
+              icon: "",
+              permissions: ["jn_dlbb_dmb"],
+            },
+          },
+          {
+            path: "rateAnalysis",
+            name: "rateAnalysis",
+            component: () =>
+              import("@/views/powerGenerating/windAnalyse/rateAnalysis"),
+            meta: {
+              title: "对风偏差分析",
+              icon: "",
+              permissions: ["jn_dlbb_dmb"],
+            },
+          },
         ],
       },
     ],

+ 7 - 6
src/views/powerGenerating/dataFilter/components/table.vue

@@ -1,7 +1,7 @@
 <script setup name="table">
 import { ref, computed, defineProps, defineEmits } from "vue";
 const props = defineProps({
-
+  showShadow: { type: Boolean, default: true },
   data: {
     type: Array,
     default: () => [],
@@ -28,10 +28,13 @@ const funExport = () => {
   emits("export");
 };
 const tableRef = ref("");
-
 </script>
 <template>
-  <div ref="tableRef" class="table-wrapper card-shadow">
+  <div
+    ref="tableRef"
+    class="table-wrapper"
+    :class="showShadow ? 'card-shadow' : ''"
+  >
     <div class="table-wrapper-title">
       <h3>{{ props.tableName }}</h3>
       <!-- <el-button size="small" type="primary" @click="funExport" :disabled="!props.tableId">数据导出</el-button> -->
@@ -64,8 +67,6 @@ const tableRef = ref("");
   height: 100%;
   padding: 20px;
   padding-top: 0;
-  width: calc(60% - 40px);
- 
-  
+  width: calc(62% - 40px);
 }
 </style>

+ 145 - 0
src/views/powerGenerating/dataFilter/lightPrepare/components/search.vue

@@ -0,0 +1,145 @@
+<script setup name="search">
+import { onMounted, reactive, ref, defineEmits } from "vue";
+import request from "@/api/axios.js";
+import SubmitBtn from "@com/SubmitBtn.vue";
+import { getGfstation, getWtByGfstation } from "@/api/powerGenerating/index.js";
+const queryForm = reactive({
+  station: "",
+  wtIds: [],
+  st: Date.now() - 30 * 24 * 60 * 60 * 1000,
+  et: Date.now(),
+  // interval: 3
+});
+/**场站 */
+const stationList = ref([]);
+const funGetStation = async () => {
+  const res = await getGfstation();
+  stationList.value = res.data;
+  if (stationList.value.length) {
+    queryForm.station = res.data[0].nemCode;
+    funGetWind(queryForm.station);
+  }
+};
+const funStationChange = (stationId) => {
+  if (stationId) {
+    funGetWind(stationId);
+  } else {
+    windList.value = [];
+  }
+};
+/**风机 */
+const checkAll = ref(true);
+const windList = ref([]);
+const funGetWind = async (stationId) => {
+  const res = await getWtByGfstation({ stationId });
+  windList.value = res.data;
+  queryForm.wtIds = res.data.map((o) => o.id);
+  checkAll.value = true;
+};
+const funCheckAll = () => {
+  checkAll.value = !checkAll.value;
+  if (checkAll.value) {
+    queryForm.wtIds = windList.value.map((o) => o.id);
+  } else {
+    queryForm.wtIds = [];
+  }
+};
+/**导出 */
+const emits = defineEmits(["submit"]);
+const funSubmit = async () => {
+  const startDate = new Date(queryForm.st).setHours(0, 0, 0, 0);
+  const endDate = new Date(queryForm.et).setHours(0, 0, 0, 0);
+  const query = {
+    station: queryForm.station,
+    wtIds: queryForm.wtIds.join(),
+    st: new Date(startDate).getTime(),
+    et: new Date(endDate).getTime(),
+    // interval: queryForm.interval
+  };
+  // switch (queryForm.interval) {
+  // 	case 2:
+  // 		query.interval = 60
+  // 		break;
+  // 	case 3:
+  // 		query.interval = 600
+  // 		break;
+  // 	case 4:
+  // 		query.interval = 900
+  // 		break;
+  // }
+  emits("submit", query);
+};
+/**created */
+funGetStation();
+</script>
+<template>
+  <div class="operation-wrapper card-shadow wrapper">
+    <div class="card-title">操作面板</div>
+    <el-form class="operation-form" :inline="true" :model="queryForm">
+      <el-form-item label="场站">
+        <el-select
+          v-model="queryForm.station"
+          size="mini"
+          popper-class="select"
+          @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="逆变器">
+        <el-select
+          multiple
+          popper-class="select"
+          size="mini"
+          v-model="queryForm.wtIds"
+          @clear="checkAll = false"
+          collapse-tags
+        >
+          <el-option
+            label="全选"
+            :class="{ selected: checkAll }"
+            @click="funCheckAll"
+          ></el-option>
+          <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="开始时间">
+        <el-date-picker
+          type="date"
+          size="mini"
+          popper-class="date-select"
+          v-model="queryForm.st"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item label="结束时间">
+        <el-date-picker
+          type="date"
+          size="mini"
+          popper-class="date-select"
+          v-model="queryForm.et"
+        ></el-date-picker>
+      </el-form-item>
+      <!-- <el-form-item label="等间隔">
+				<el-radio-group v-model="queryForm.interval">
+					<el-radio :label="1">一秒钟</el-radio>
+					<el-radio :label="2">一分钟</el-radio>
+					<el-radio :label="3">十分钟</el-radio>
+					<el-radio :label="4">十五分钟</el-radio>
+				</el-radio-group>
+			</el-form-item> -->
+      <el-form-item>
+        <submit-btn v-prevdbclick:5000="funSubmit" desc="执行"></submit-btn>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>

+ 53 - 0
src/views/powerGenerating/dataFilter/lightPrepare/components/table.vue

@@ -0,0 +1,53 @@
+<script setup name="table">
+import {ref, computed} from 'vue'
+const props = defineProps({
+  height: {
+    type: String,
+    default: '800px'
+  },
+  data: {
+    type: Array,
+    default: () => ([]),
+  },
+  column: {
+    type: Array,
+    default: () => ([]),
+  },
+  tableName: {
+    type: String,
+    default: '',
+  },
+  tableId: {
+    type: String,
+    default: '',
+  },
+  loading: {
+    type: Boolean,
+    default: false,
+  }
+})
+const emits = defineEmits(['export'])
+const funExport = () => {
+  emits('export')
+}
+const tableRef = ref('')
+const tableHeight =  computed(() => {
+  return tableRef.value.offsetHeight? tableRef.value.offsetHeight - 46 : 739
+})
+</script>
+<template>
+  <div ref="tableRef" class="p-[10px] shadow rounded-[6px] shadow-blue-500"
+    :style="{ height: typeof props.height === 'string' ? props.height : props.height + 'px' }">
+    <div class="flex justify-between items-center pb-[10px]">
+      <h3>{{props.tableName}}</h3>
+      <!-- <el-button size="small" type="primary" @click="funExport" :disabled="!props.tableId">数据导出</el-button> -->
+    </div>
+    <el-table :data="props.data"
+      stripe
+      size="small" v-loading="props.loading"
+      :max-height="tableHeight"
+      :style="{ width: '100%'}">
+      <el-table-column align="center" show-overflow-tooltip v-for="item in props.column" :prop="item.prop" :label="item.label" sortable resizable :min-width="item.width? item.width : 80" />
+    </el-table>
+  </div>
+</template>

+ 190 - 0
src/views/powerGenerating/dataFilter/lightPrepare/index.vue

@@ -0,0 +1,190 @@
+<script setup name="prepare">
+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 { ElMessage } from "element-plus";
+import { onMounted, ref, onActivated } from "vue";
+import {
+  lightPrepareExcelShow,
+  getLightPrepareTree,
+  getLightPrepareData,
+} from "@/api/powerGenerating/index.js";
+const baseURL = process.env.VUE_APP_TEST;
+const currentId = ref("");
+/**excel 开始 */
+const excelList = ref([]);
+const funExcelChange = async (obj) => {
+  //点击excel项时
+  tableShowId.value = obj.id;
+  currentId.value = obj.id;
+  tableName.value = obj.name;
+  tableLoading.value = true;
+  const res = await lightPrepareExcelShow({ filename: obj.id });
+  if (res.code === 200) {
+    tableColumn.value = res.data.title.map((o) => {
+      return {
+        prop: o.key,
+        label: o.des,
+        width: o.des === "时间" ? 100 : 80,
+      };
+    });
+    tableData.value = res.data.data;
+    tableLoading.value = false;
+  } else {
+    tableLoading.value = false;
+  }
+};
+/**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.path);
+      if (findIndex !== -1) {
+        o.childs = o.children;
+        o.children = [];
+        if (!actTreeNode.value) {
+          //判断当且仅有process获取tree时 赋值
+          actTreeNode.value = o;
+        }
+      }
+    }
+    return {
+      ...o,
+      children: o.children?.length ? funRepeatMap(o.children) : [],
+    };
+  });
+};
+const funGetTree = async () => {
+  actTreeNode.value = null;
+  const res = await getLightPrepareTree();
+  treeData.value = funRepeatMap(res.data);
+  excelList.value = [];
+  if (actTreeNode.value) {
+    funCurrentChange({ current: actTreeNode.value, currentNode: null });
+    const child = actTreeNode.value.childs[0];
+    const obj = {
+      id: child.path,
+      interval: child.interval,
+      path: child.path,
+      station: child.station,
+      time: child.time,
+      type: child.type,
+      windturbineId: child.windturbineId,
+      name: child.path,
+    };
+    funExcelChange(obj);
+  }
+};
+const funCurrentChange = ({ current, currentNode }) => {
+  if (current.childs) {
+    excelList.value = current.childs.map((o) => {
+      return {
+        id: o.path,
+        interval: o.interval,
+        path: o.path,
+        station: o.station,
+        time: o.time,
+        type: o.type,
+        windturbineId: o.windturbineId,
+        name: o.path,
+      };
+    });
+  } else {
+    excelList.value = [];
+  }
+};
+/**table 开始 */
+const tableShowId = ref("");
+const tableName = ref("");
+const tableColumn = ref([]);
+const tableLoading = ref(false);
+const tableData = ref([]);
+const funExport = async () => {
+  const a = document.createElement("a");
+  a.href = baseURL + "/power/prepare/download?id=" + tableShowId.value;
+  a.download = "";
+  a.click();
+};
+/**submit */
+const progress = ref(0);
+/** 
+const funWebSocket = () => {
+	const webSocket = new WebSocket(`${socketURL}/ws/powerfitting/admin`)
+	webSocket.onerror = () => setTimeout(() => { funWebSocket() }, 2000)
+
+	webSocket.onmessage = (event) => {
+		const message = JSON.parse(event.data)
+		if (message.code === 200) {
+			progress.value = Number(message.data) * 100
+			if (progress.value === 100) {
+				ElMessage.success('数据加载完成')
+				funGetTree()
+				progress.value = 0
+			}
+		}
+	}
+}
+*/
+const funSubmit = async (params) => {
+  const res = await getLightPrepareData({
+    station: params.station,
+    inverters: params.wtIds,
+    startdate: params.st,
+    enddate: params.et,
+  });
+  if (res.code === 200) {
+    ElMessage.success(res.msg);
+    funGetTree();
+  }
+};
+/**created */
+funGetTree();
+// funWebSocket()
+/**mounted */
+
+/**activated */
+onActivated(() => {
+  funGetTree();
+});
+</script>
+<template>
+  <div class="container-wrapper">
+    <search-cop @submit="funSubmit"> </search-cop>
+    <div class="power-data-wrapper card-shadow wrapper">
+      <div class="card-title">数据展示</div>
+      <div class="data-wrapper">
+        <tree-cop
+          :data="treeData"
+          @currentChange="funCurrentChange"
+          @refresh="funGetTree"
+        /><excel-cop
+          :data="excelList"
+          :currentIds="currentId"
+          @excelChange="funExcelChange"
+        />
+        <table-cop
+          class=""
+          :data="tableData"
+          :column="tableColumn"
+          :loading="tableLoading"
+          :tableId="tableShowId"
+          :tableName="tableName"
+          @export="funExport"
+        />
+      </div>
+    </div>
+
+    <el-progress
+      :percentage="progress"
+      v-if="progress"
+      class="!absolute top-0 right-0 left-0"
+      :indeterminate="false"
+      color="rgb(19,206,102)"
+      :stroke-width="4"
+      :show-text="false"
+    />
+  </div>
+</template>

+ 54 - 3
src/views/powerGenerating/index.vue

@@ -25,12 +25,12 @@ export default {
 }
 //发电能力分析模块公共样式
 .container-wrapper {
-  height: calc(100% - 10px * 2);
+  height: 100%;
   width: calc(100% - 20px * 2);
   margin: 0 20px;
   padding: 10px 0;
   .power-data-wrapper {
-    height: calc(100% - 58px - 20px);
+    height: calc(100% - 58px - 20px - 20px);
     margin-top: 20px;
     width: 100%;
     background: #161f1e;
@@ -59,6 +59,58 @@ export default {
           width: 100%;
         }
       }
+      .data-excel-wrapper {
+        display: flex;
+        width: 22%;
+        flex-direction: column;
+        justify-content: space-between;
+        margin-right: 20px;
+        .excel-height {
+          height: calc(50% - 10px);
+          width: 100%;
+        }
+        .excel-wrapper {
+          width: 100%;
+          margin-right: 0;
+        }
+      }
+      .data-chart-wrapper {
+        width: calc(62% - 40px);
+        height: 100%;
+        display: flex;
+        flex-wrap: wrap;
+        justify-content: space-around;
+        .data-chart-item {
+          width: calc(50% - 10px);
+          height: calc(50% - 10px);
+          padding: 10px;
+          position: relative;
+          .zoom-icon{
+            position:absolute;
+            right: 10px;
+            top: 10px;
+            z-index: 2;
+            cursor: pointer;
+          }
+        }
+        .mb-10 {
+          margin-bottom: 10px;
+        }
+      }
+      .data-table-wrapper {
+        width: calc(58% - 40px);
+        height: 100%;
+        display: flex;
+        flex-direction: column;
+        padding: 10px;
+        position: relative;
+        .submit-btn {
+          position: absolute;
+          z-index: 2;
+          top: 10px;
+          right: 10px;
+        }
+      }
     }
   }
 }
@@ -67,7 +119,6 @@ export default {
   background: #161f1e;
   padding: 20px;
   padding-bottom: 12px;
- 
 }
 ::v-deep.operation-form .el-checkbox {
   .el-checkbox__input {

+ 398 - 0
src/views/powerGenerating/windAnalyse/combine/components/current-scatter-chart.json

@@ -0,0 +1,398 @@
+
+{
+	"color": [
+			"#1C99FF",
+			"#FF8700",
+			"#e6b600d9",
+			"#0098d9",
+			"#3D54BE",
+			"#005eaa",
+			"#cda819",
+			"#32a487"
+	],
+	"textStyle": {},
+	"title": {
+			"textStyle": {
+					"color": "#333333"
+			},
+			"subtextStyle": {
+					"color": "#aaaaaa"
+			}
+	},
+	"line": {
+			"itemStyle": {
+					"borderWidth": 1
+			},
+			"lineStyle": {
+					"width": 2
+			},
+			"symbolSize": 4,
+			"symbol": "emptyCircle",
+			"smooth": false
+	},
+	"radar": {
+			"itemStyle": {
+					"borderWidth": 1
+			},
+			"lineStyle": {
+					"width": 2
+			},
+			"symbolSize": 4,
+			"symbol": "emptyCircle",
+			"smooth": false
+	},
+	"bar": {
+			"itemStyle": {
+					"barBorderWidth": 0,
+					"barBorderColor": "#ccc"
+			}
+	},
+	"pie": {
+			"itemStyle": {
+					"borderWidth": 0,
+					"borderColor": "#ccc"
+			}
+	},
+	"scatter": {
+			"itemStyle": {
+					"borderWidth": 0,
+					"borderColor": "#ccc"
+			}
+	},
+	"boxplot": {
+			"itemStyle": {
+					"borderWidth": 0,
+					"borderColor": "#ccc"
+			}
+	},
+	"parallel": {
+			"itemStyle": {
+					"borderWidth": 0,
+					"borderColor": "#ccc"
+			}
+	},
+	"sankey": {
+			"itemStyle": {
+					"borderWidth": 0,
+					"borderColor": "#ccc"
+			}
+	},
+	"funnel": {
+			"itemStyle": {
+					"borderWidth": 0,
+					"borderColor": "#ccc"
+			}
+	},
+	"gauge": {
+			"itemStyle": {
+					"borderWidth": 0,
+					"borderColor": "#ccc"
+			}
+	},
+	"candlestick": {
+			"itemStyle": {
+					"color": "#c12e34",
+					"color0": "#2b821d",
+					"borderColor": "#c12e34",
+					"borderColor0": "#2b821d",
+					"borderWidth": 1
+			}
+	},
+	"graph": {
+			"itemStyle": {
+					"borderWidth": 0,
+					"borderColor": "#ccc"
+			},
+			"lineStyle": {
+					"width": 1,
+					"color": "#aaaaaa"
+			},
+			"symbolSize": 4,
+			"symbol": "emptyCircle",
+			"smooth": false,
+			"color": [
+					"#c12e34",
+					"#e6b600",
+					"#0098d9",
+					"#50ec39",
+					"#005eaa",
+					"#339ca8",
+					"#cda819",
+					"#32a487"
+			],
+			"label": {
+					"color": "#eeeeee"
+			}
+	},
+	"map": {
+			"itemStyle": {
+					"areaColor": "#ddd",
+					"borderColor": "#eee",
+					"borderWidth": 0.5
+			},
+			"label": {
+					"color": "#c12e34"
+			},
+			"emphasis": {
+					"itemStyle": {
+							"areaColor": "#e6b600",
+							"borderColor": "#ddd",
+							"borderWidth": 1
+					},
+					"label": {
+							"color": "#c12e34"
+					}
+			}
+	},
+	"geo": {
+			"itemStyle": {
+					"areaColor": "#ddd",
+					"borderColor": "#eee",
+					"borderWidth": 0.5
+			},
+			"label": {
+					"color": "#c12e34"
+			},
+			"emphasis": {
+					"itemStyle": {
+							"areaColor": "#e6b600",
+							"borderColor": "#ddd",
+							"borderWidth": 1
+					},
+					"label": {
+							"color": "#c12e34"
+					}
+			}
+	},
+	"categoryAxis": {
+			"axisLine": {
+					"show": true,
+					"lineStyle": {
+							"color": "#333"
+					}
+			},
+			"axisTick": {
+					"show": true,
+					"lineStyle": {
+							"color": "#333"
+					}
+			},
+			"axisLabel": {
+					"show": true,
+					"color": "#333"
+			},
+			"splitLine": {
+					"show": false,
+					"lineStyle": {
+							"color": [
+									"#ccc"
+							]
+					}
+			},
+			"splitArea": {
+					"show": false,
+					"areaStyle": {
+							"color": [
+									"rgba(250,250,250,0.3)",
+									"rgba(200,200,200,0.3)"
+							]
+					}
+			}
+	},
+	"valueAxis": {
+			"axisLine": {
+					"show": true,
+					"lineStyle": {
+							"color": "#838383"
+					}
+			},
+			"axisTick": {
+					"show": true,
+					"lineStyle": {
+							"color": "#838383"
+					}
+			},
+			"axisLabel": {
+					"show": true,
+					"color": "#838383"
+			},
+			"splitLine": {
+					"show": true,
+					"lineStyle": {
+							"color": [
+									"#ccc"
+							]
+					}
+			},
+			"splitArea": {
+					"show": false,
+					"areaStyle": {
+							"color": [
+									"rgba(250,250,250,0.3)",
+									"rgba(200,200,200,0.3)"
+							]
+					}
+			}
+	},
+	"logAxis": {
+			"axisLine": {
+					"show": true,
+					"lineStyle": {
+							"color": "#333"
+					}
+			},
+			"axisTick": {
+					"show": true,
+					"lineStyle": {
+							"color": "#333"
+					}
+			},
+			"axisLabel": {
+					"show": true,
+					"color": "#333"
+			},
+			"splitLine": {
+					"show": true,
+					"lineStyle": {
+							"color": [
+									"#ccc"
+							]
+					}
+			},
+			"splitArea": {
+					"show": false,
+					"areaStyle": {
+							"color": [
+									"rgba(250,250,250,0.3)",
+									"rgba(200,200,200,0.3)"
+							]
+					}
+			}
+	},
+	"timeAxis": {
+			"axisLine": {
+					"show": true,
+					"lineStyle": {
+							"color": "#333"
+					}
+			},
+			"axisTick": {
+					"show": true,
+					"lineStyle": {
+							"color": "#333"
+					}
+			},
+			"axisLabel": {
+					"show": true,
+					"color": "#333"
+			},
+			"splitLine": {
+					"show": true,
+					"lineStyle": {
+							"color": [
+									"#ccc"
+							]
+					}
+			},
+			"splitArea": {
+					"show": false,
+					"areaStyle": {
+							"color": [
+									"rgba(250,250,250,0.3)",
+									"rgba(200,200,200,0.3)"
+							]
+					}
+			}
+	},
+	"toolbox": {
+			"iconStyle": {
+					"borderColor": "#06467c"
+			},
+			"emphasis": {
+					"iconStyle": {
+							"borderColor": "#4187c2"
+					}
+			},
+			"textStyle": {
+				"color": "#838383"
+			}
+	},
+	"legend": {
+			"textStyle": {
+					"color": "#838383"
+			}
+	},
+	"tooltip": {
+			"axisPointer": {
+					"lineStyle": {
+							"color": "#cccccc",
+							"width": 1
+					},
+					"crossStyle": {
+							"color": "#cccccc",
+							"width": 1
+					}
+			}
+	},
+	"timeline": {
+			"lineStyle": {
+					"color": "#005eaa",
+					"width": 1
+			},
+			"itemStyle": {
+					"color": "#005eaa",
+					"borderWidth": 1
+			},
+			"controlStyle": {
+					"color": "#005eaa",
+					"borderColor": "#005eaa",
+					"borderWidth": 0.5
+			},
+			"checkpointStyle": {
+					"color": "#005eaa",
+					"borderColor": "#316bc2"
+			},
+			"label": {
+					"color": "#005eaa"
+			},
+			"emphasis": {
+					"itemStyle": {
+							"color": "#005eaa"
+					},
+					"controlStyle": {
+							"color": "#005eaa",
+							"borderColor": "#005eaa",
+							"borderWidth": 0.5
+					},
+					"label": {
+							"color": "#005eaa"
+					}
+			}
+	},
+	"visualMap": {
+			"color": [
+					"#1790cf",
+					"#a2d4e6"
+			]
+	},
+	"dataZoom": {
+			"backgroundColor": "rgba(47,69,84,0)",
+			"dataBackgroundColor": "rgba(47,69,84,0.3)",
+			"fillerColor": "rgba(167,183,204,0.4)",
+			"handleColor": "#a7b7cc",
+			"handleSize": "100%",
+			"textStyle": {
+					"color": "#333333"
+			}
+	},
+	"markPoint": {
+			"label": {
+					"color": "#eeeeee"
+			},
+			"emphasis": {
+					"label": {
+							"color": "#eeeeee"
+					}
+			}
+	}
+}

+ 359 - 0
src/views/powerGenerating/windAnalyse/combine/components/current-scatter-chart.vue

@@ -0,0 +1,359 @@
+<template>
+  <div class="chart" :id="id"></div>
+</template>
+
+<script>
+import util from "@tools/util";
+import partten from "@/helper/partten";
+import * as echarts from "echarts";
+import chartTheme from "./current-scatter-chart.json";
+
+export default {
+  name: "currentScatterChart",
+  props: {
+    // 图表宽度
+    width: {
+      type: String,
+      default: "100%",
+    },
+    // 图表高度
+    height: {
+      type: String,
+      default: "350px",
+    },
+    // 图表主标题
+    chartTitle: {
+      type: String,
+      default: "自定义图表组件",
+    },
+    // X 轴配置项
+    xAxisData: {
+      type: Array,
+      default: () => {
+        return [];
+      },
+    },
+    // Y 轴配置项
+    yAxisData: {
+      type: Array,
+      default: () => {
+        return [];
+      },
+    },
+    dataSet: {
+      type: String,
+      default: "",
+    },
+    // 图表核心数据
+    seriesData: {
+      type: Array,
+      default: () => {
+        return [];
+      },
+    },
+    // 是否显示图表图例
+    showLegend: {
+      type: Boolean,
+      default: true,
+    },
+    // 是否默认采用笔刷模式
+    brushSelected: {
+      type: Boolean,
+      default: false,
+    },
+  },
+  data() {
+    return {
+      id: "",
+      chart: null,
+      color: [
+        "#05bb4c",
+        "#4b55ae",
+        "#fa8c16",
+        "#f8de5b",
+        "#1a93cf",
+        "#c531c7",
+        "#bd3338",
+      ],
+      theme: "dark",
+    };
+  },
+  computed: {
+    collapse() {
+      return this.$store.state.collapse;
+    },
+  },
+  watch: {
+    height() {
+      if (this.chart) {
+        this.chart.resize();
+      }
+    },
+    collapse(val) {
+      if (this.chart) {
+        setTimeout(() => {
+          this.chart.resize();
+        }, 300);
+      }
+    },
+  },
+  methods: {
+    resize() {},
+    initChart() {
+      const that = this;
+      echarts.registerTheme("chartTheme", chartTheme);
+      let myChart = echarts.init(
+        document.getElementById(this.id),
+        "chartTheme"
+      );
+      that.chart = myChart;
+      //指定图表的配置项和数据
+      const option = {
+        //标题
+        title: {
+          text: that.chartTitle,
+          right: 440,
+          top: 4,
+          textStyle: {
+            fontSize: 14,
+            color: that.theme === "dark" ? partten.getColor("grayl") : "#000",
+          },
+        },
+        // backgroundColor:
+        //   that.theme === "dark"
+        //     ? "rgba(0,0,0,0.4)"
+        //     : "rgba(255,255,255,0.5)",
+        //工具箱
+        color: [
+          "#3D54BE",
+          "rgb(255,0,0)",
+          "#a1a1a1",
+          "#0098d9",
+          "#FF8700",
+          "#005eaa",
+          "#cda819",
+          "#32a487",
+        ],
+        toolbox: {
+          show: true,
+          x: "right",
+          position: [10, 10],
+          // backgroundColor:'rgba(0,0,0,0.4)',
+          borderColor: partten.getColor("gray"),
+          textStyle: {
+            fontSize: util.vh(16),
+            color: partten.getColor("gray"),
+          },
+          iconStyle: {
+            borderColor: partten.getColor("gray"),
+          },
+          emphasis: {
+            iconStyle: {
+              borderColor: partten.getColor("gray"),
+            },
+          },
+        },
+        tooltip: {
+          trigger: "item",
+          axisPointer: {
+            type: "cross",
+          },
+          backgroundColor: "rgba(0,0,0,0.4)",
+          borderColor: partten.getColor("gray"),
+          textStyle: {
+            fontSize: util.vh(16),
+            color: "#fff",
+          },
+          formatter(params) {
+            return params.value?.x
+              ? `${params.seriesName}<br />风速:${params.value.x} m/s<br />功率:${params.value.y} kW`
+              : `${params.name}`;
+          },
+        },
+        brush: {
+          seriesIndex: [2, 3],
+          yAxisIndex: 0,
+          transformable: true,
+          throttleType: "debounce",
+          throttleDelay: 1000,
+          removeOnClick: true,
+          brushType: "polygon",
+          brushMode: "multiple",
+          brushStyle: {
+            borderWidth: 1,
+            borderColor: "#ff2424",
+          },
+        },
+        dataZoom: [
+          {
+            type: "inside", //图表下方的伸缩条
+            show: false, //是否显示
+            realtime: true, //拖动时,是否实时更新系列的视图
+            start: 0, //伸缩条开始位置(1-100),可以随时更改
+            end: 100, //伸缩条结束位置(1-100),可以随时更改
+          },
+          {
+            type: "slider", //图表下方的伸缩条
+            show: false, //是否显示
+            realtime: true, //拖动时,是否实时更新系列的视图
+            start: 0, //伸缩条开始位置(1-100),可以随时更改
+            end: 100, //伸缩条结束位置(1-100),可以随时更改
+          },
+        ],
+        textStyle: {
+          fontSize: util.vh(16),
+          color: that.theme === "dark" ? "#fff" : "#000",
+        },
+        //图例-每一条数据的名字
+        legend: {
+          show: that.showLegend,
+          data: ["拟合功率", "保证功率", "无用点", "有用点", "Cp值"],
+          right: "120",
+          top: "5",
+          // icon: "circle",
+          itemWidth: 6,
+          inactiveColor:
+            that.theme === "dark" ? partten.getColor("gray") : "#000",
+          textStyle: {
+            color: that.theme === "dark" ? partten.getColor("grayl") : "#000",
+            fontSize: 12,
+          },
+        },
+        grid: {
+          top: 48,
+          left: 40,
+          right: 40,
+          bottom: 24,containLabel: true,
+        },
+        //x轴
+        xAxis: [
+          {
+            name: "m/s",
+            nameTextStyle: {
+              color: "#838383",
+            },
+            type: "value",
+            boundaryGap: false,
+            data: that.xAxisData || [],
+            min: 0,
+            max: 25,
+            interval: 1,
+            axisLabel: {
+              formatter: "{value}",
+            },
+            splitLine: {
+              show: false,
+            },
+            textStyle: {
+              color: that.theme === "dark" ? partten.getColor("gray") : "#000",
+            },
+          },
+        ],
+        //y轴没有显式设置,根据值自动生成y轴
+        yAxis: [
+          {
+            splitLine: { show: false },
+            position: "left",
+            min: 0,
+            name: "kW",
+            nameTextStyle: {
+              color: "#838383",
+            },
+          },
+          {
+            splitLine: { show: false },
+            position: "right",
+            min: 0,
+          },
+        ],
+        animation: true,
+        dataset: that.dataSet.length ? JSON.parse(that.dataSet) : [],
+        //数据-data是最终要显示的数据
+        series: that.seriesData,
+      };
+
+      that.resize = function () {
+        myChart.resize();
+      };
+
+      window.addEventListener("resize", that.resize);
+
+      myChart.setOption(option);
+      if (that.brushSelected) {
+        myChart.dispatchAction({
+          type: "takeGlobalCursor",
+          // 如果想变为“可刷选状态”,必须设置。不设置则会关闭“可刷选状态”。
+          key: "brush",
+          brushOption: {
+            seriesIndex: [2, 3],
+            yAxisIndex: 0,
+            transformable: true,
+            throttleType: "debounce",
+            throttleDelay: 1000,
+            removeOnClick: true,
+            brushType: "polygon",
+            brushMode: "multiple",
+            brushStyle: {
+              borderWidth: 1,
+              color: "rgba(255,36,36,0.2)",
+              borderColor: "#ff2424",
+            },
+          },
+        });
+      }
+      myChart.off("brushSelected");
+      myChart.on("brushSelected", (params) => {
+        that.$emit("getSelected", params.batch || []);
+      });
+      myChart.off("click");
+      myChart.on("click", (params) => {
+        // console.log(params)
+        if (params.componentType === "markArea") {
+          myChart.dispatchAction({
+            type: "brush",
+            areas: [
+              {
+                xAxisIndex: 0,
+                brushType: "lineX",
+                coordRange: [params.data.coord[0][0], params.data.coord[1][0]],
+              },
+            ],
+          });
+        }
+      });
+    },
+  },
+  created() {
+    this.id = "chart-" + util.newGUID();
+  },
+  mounted() {
+    // this.$nextTick(() => {
+    this.$el.style.width = this.width;
+    this.$el.style.height = this.height;
+    this.initChart();
+    if (this.chart) {
+      this.chart.resize();
+    }
+    // });
+  },
+  updated() {
+    // console.log('update')
+    let myChart = echarts.init(document.getElementById(this.id));
+    myChart.dispose();
+    this.$nextTick(() => {
+      this.initChart();
+    });
+  },
+  unmounted() {
+    window.removeEventListener("resize", this.resize);
+  },
+};
+</script>
+
+<style>
+.chart {
+  width: 100%;
+  height: 100%;
+  display: inline-block;
+}
+</style>

+ 73 - 0
src/views/powerGenerating/windAnalyse/combine/components/search.vue

@@ -0,0 +1,73 @@
+<script setup name="search">
+import { reactive, ref, defineEmits } from "vue";
+import SubmitBtn from "@com/SubmitBtn.vue";
+
+const queryForm = reactive({
+  maxs: 25,
+  mins: 0,
+  maxp: 2500,
+  minp: 0,
+  dimension: 10, //拟合维度
+  mode: 0, //拟合方式
+});
+/**导出 */
+const emits = defineEmits(["submit"]);
+const funSubmit = async () => {
+  emits("submit", queryForm);
+};
+/**created */
+</script>
+<template>
+  <div class="operation-wrapper card-shadow wrapper">
+    <div class="card-title">操作面板</div>
+    <el-form class="operation-form" :inline="true" :model="queryForm">
+      <el-form-item label="最大风速">
+        <el-input-number
+          v-model="queryForm.maxs"
+          size="small"
+          :max="30"
+        ></el-input-number>
+      </el-form-item>
+      <el-form-item label="最小风速">
+        <el-input-number
+          v-model="queryForm.mins"
+          size="small"
+          :min="0"
+        ></el-input-number>
+      </el-form-item>
+      <el-form-item label="最大功率">
+        <el-input-number
+          v-model="queryForm.maxp"
+          size="small"
+        ></el-input-number>
+      </el-form-item>
+      <el-form-item label="最小功率">
+        <el-input-number
+          v-model="queryForm.minp"
+          size="small"
+          :min="0"
+        ></el-input-number>
+      </el-form-item>
+      <el-form-item label="多项式">
+        <el-select v-model="queryForm.dimension" size="mini">
+          <el-option
+            v-for="item in 30"
+            :key="item"
+            :value="item"
+            :label="item"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="拟合方式">
+        <el-select v-model="queryForm.mode" size="mini">
+          <el-option :value="0" label="单台拟合"></el-option>
+          <el-option :value="1" label="合并拟合"></el-option>
+          <el-option :value="2" label="同名拟合"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <submit-btn @click="funSubmit" desc="曲线拟合"></submit-btn>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>

+ 810 - 0
src/views/powerGenerating/windAnalyse/combine/index.vue

@@ -0,0 +1,810 @@
+<script setup name="combine">
+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 {
+  getWindFittingExcelShow,
+  getWindFittingData,
+  getProcessData,
+  getProcessTree,
+  getWindFittingTree,
+  getWindFittingShow,
+  getWindFittingFilter,
+  getWindFittingAlarms,
+} from "@/api/powerGenerating/index.js";
+import SubmitBtn from "@/components/SubmitBtn.vue";
+import { ref, nextTick, onActivated, onMounted, reactive } from "vue";
+import { ElMessage } from "element-plus";
+import CurrentScatterChart from "./components/current-scatter-chart.vue";
+const currentId = ref("");
+/**excel 开始 */
+const excelCheckboxShow = ref(false);
+const excelType = ref("");
+const excelCheckIds = ref([]);
+const excelList = ref([]);
+const funExcelChange = async (obj) => {
+  //点击excel项时
+  activeTab.value = "1";
+  isChartArea.value = false;
+  tableShowId.value = obj.id;
+  currentId.value = obj.id;
+  tableName.value = obj.name;
+  excelType.value = obj.type; // 接收excel的type 用于控制右侧tab展示
+
+  let res = null;
+  let chartRes = {
+    scatterhs: [[]],
+    scatterls: [[]],
+    sjgl: [[]],
+    llgl: [[]],
+    cpz: [[]],
+  };
+  let chartResponse = null;
+  tableLoading.value = true;
+  if (obj.type === "process") {
+    res = await getProcessData({ id: obj.id });
+  } else if (obj.type === "fitting") {
+    activeTab.value = "2";
+    res = await getWindFittingShow({ id: obj.id });
+    // res = tableRes
+    // chartResponse = dotRes
+    chartResponse = await getWindFittingExcelShow({ id: obj.id, p: 1 });
+  }
+  if (res.code === 200) {
+    tableColumn.value = res.data.title.map((o) => {
+      return {
+        prop: o.key,
+        width: o.des === "时间" ? 100 : 80,
+        label: o.des,
+      };
+    });
+    tableData.value = res.data.data;
+    tableLoading.value = false;
+  } else {
+    tableLoading.value = false;
+  }
+
+  if (chartResponse && chartResponse.code === 200) {
+    chartRes = chartResponse.data;
+    markDot.pcl5 = chartRes.obj.pc5ratio;
+    markDot.pcl10 = chartRes.obj.pc10ratio;
+    markDot.pcl12 = chartRes.obj.pc12ratio;
+    markDot.pcl25 = chartRes.obj.pc25ratio;
+    avgObj.title = chartRes.obj.path
+      .substring(
+        chartRes.obj.path.indexOf(chartRes.obj.station + "_") +
+          (chartRes.obj.station + "_").length
+      )
+      .split("_")[0];
+    avgObj.cpavg = Number(chartRes.obj.cpavg).toFixed(2);
+    avgObj.frequency = Number(chartRes.obj.frequency).toFixed(2);
+    avgObj.pcratio = Number(chartRes.obj.pcratio).toFixed(2);
+    dataSet.value = JSON.stringify([
+      {
+        source: chartRes.wyd,
+        // source: chartRes.scatterls
+      },
+      {
+        source: chartRes.yyd,
+        // source: chartRes.scatterhs
+      },
+    ]);
+    const color = [
+      "#1C99FF",
+      "#FF8700",
+      "#3D54BE",
+      "#fa8c16",
+      "#1DA0D7",
+      "#DD5044",
+    ];
+    seriesData.value = [
+      {
+        name: "拟合功率",
+        type: "line",
+        symbol: "line", //设定为实心点
+        symbolSize: 0, //设定实心点的大小
+        smooth: true, //这个是把线变成曲线
+        data: chartRes.sjgl,
+        xAxisIndex: 0,
+      },
+      {
+        name: "保证功率",
+        type: "line",
+        symbol: "line", //设定为实心点
+        symbolSize: 0, //设定实心点的大小
+        smooth: true, //这个是把线变成曲线
+        data: chartRes.llgl,
+        xAxisIndex: 0,
+      },
+      {
+        type: "effectScatter",
+        showEffectOn: "emphasis",
+        rippleEffect: {
+          scale: 1,
+        },
+        name: "无用点",
+        symbolSize: (data) => {
+          return data.s ? (data.s > 10 ? 10 : data.s) : 4;
+        },
+        datasetIndex: 0,
+        encode: {
+          x: "x",
+          y: "y",
+        },
+        xAxisIndex: 0,
+        yAxisIndex: 0,
+      },
+      {
+        type: "effectScatter",
+        showEffectOn: "emphasis",
+        rippleEffect: {
+          scale: 1,
+        },
+        name: "有用点",
+        symbolSize: (data) => {
+          return data.s ? (data.s > 10 ? 10 : data.s) : 4;
+        },
+        datasetIndex: 1,
+        encode: {
+          x: "x",
+          y: "y",
+        },
+        xAxisIndex: 0,
+        yAxisIndex: 0,
+      },
+      {
+        name: "Cp值",
+        type: "line",
+        symbol: "line", //设定为实心点
+        symbolSize: 0, //设定实心点的大小
+        smooth: true, //这个是把线变成曲线
+        data: chartRes.cpz,
+        xAxisIndex: 0,
+        yAxisIndex: 1,
+      },
+    ];
+  }
+};
+const funExcelCheckChange = ({ checkArr, data }) => {
+  //bug
+  excelCheckIds.value = checkArr;
+};
+/**excel fitData */
+const excelFitList = ref([]);
+/**prepare tree 开始 */
+const treeData = ref([]);
+const actTreeNode = ref(null); //当前激活的treeNode
+const funRepeatMap = (arr, type = "prepare") => {
+  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 && type === "process") {
+          //判断当且仅有process获取tree时 赋值
+          actTreeNode.value = o;
+        }
+      }
+    }
+    return {
+      ...o,
+      children: o.children ? funRepeatMap(o.children, type) : [],
+    };
+  });
+};
+const funGetTree = async () => {
+  const res = await getProcessTree();
+  treeData.value = funRepeatMap(res.data);
+  excelList.value = [];
+};
+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;
+};
+
+/**process tree 开始 */
+const processTreeData = ref([]);
+const funGetProcessTree = async (flag = true) => {
+  //flag控制是否获取tree的第一项 true为可获取
+  actTreeNode.value = null;
+  const res = await getWindFittingTree();
+  excelFitList.value = [];
+  processTreeData.value = funRepeatMap(res.data, flag ? "process" : "prepare"); //flag控制对actTreeNode赋值
+  if (actTreeNode.value) {
+    funProcessCurrentChange({ current: actTreeNode.value, currentNode: null });
+    const child = actTreeNode.value.childs[0];
+    const obj = {
+      id: child.id,
+      interval: child.interval,
+      path: child.path,
+      prepareid: child.prepareid,
+      station: child.station,
+      time: child.time,
+      type: child.type,
+      windturbine: child.windturbine,
+      name: child.path.substring(
+        child.path.indexOf(child.station + "_") + (child.station + "_").length
+      ),
+    };
+    funExcelChange(obj);
+  }
+};
+const funProcessCurrentChange = ({ current, currentNode }) => {
+  if (current.childs) {
+    excelFitList.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 {
+    excelFitList.value = [];
+  }
+};
+
+/**table 开始 */
+const tableShowId = ref("");
+const tableColumn = ref([]);
+const tableLoading = ref(false);
+const tableName = ref("");
+const tableData = ref([]);
+/**table 结束 */
+/**search 开始 */
+const funSubmit = async (query) => {
+  if (!excelCheckIds.value.length) {
+    ElMessage.error("请勾选要预处理的项");
+    return false;
+  }
+  const params = {
+    ...query,
+    ids: excelCheckIds.value.join(","),
+  };
+  const res = await getWindFittingData(params);
+  if (res.code === 200) {
+    ElMessage.success(res.msg);
+    funGetProcessTree(false); //阻止获取tree第一项数据及图表
+    const excelInfo = res.data;
+
+    /**拟合完成后 显示右侧图表及数据 */
+    funExcelChange({
+      id: excelInfo.id,
+      name: excelInfo.path.substring(
+        excelInfo.path.indexOf(excelInfo.station + "_") +
+          (excelInfo.station + "_").length
+      ),
+      type: "fitting",
+    });
+  }
+};
+/**chart Data */
+const avgObj = reactive({
+  //平均cpz等
+  title: "",
+  cpavg: "",
+  frequency: "",
+  pcratio: "",
+});
+const markDot = reactive({
+  //3-5 point点等
+  pcl5: null,
+  pcl10: null,
+  pcl12: null,
+  pcl25: null,
+});
+const xAxisData = ref([]);
+const chartRef = ref(); //chart 的ref
+const seriesData = ref([]);
+const isChartArea = ref(false); // 用来控制图表是否区域划分
+const dataSet = ref("");
+const funChartSelect = async (batch) => {
+  const wDataArr = [];
+  const yDataArr = [];
+  let scatterls = [];
+  let scatterhs = [];
+  let dataSetObj = [];
+  wtData.value = [];
+  if (batch?.length && dataSet.value) {
+    scatterls = batch[0].selected[2].dataIndex;
+    scatterhs = batch[0].selected[3].dataIndex;
+    if (scatterls?.length || scatterhs?.length) {
+      dataSetObj = JSON.parse(dataSet.value);
+      if (scatterls?.length) {
+        for (const scatterIndex of scatterls) {
+          wDataArr.push(dataSetObj[0].source[scatterIndex].k);
+        }
+      }
+      if (scatterhs?.length) {
+        for (const scatterIndex of scatterhs) {
+          yDataArr.push(dataSetObj[1].source[scatterIndex].k);
+        }
+      }
+      const wtRes = await getWindFittingFilter({
+        yk: yDataArr.join(","),
+        wk: wDataArr.join(","),
+        only: dataRadom.value,
+      });
+      if (wtRes.code === 200) {
+        let id = 1;
+        const tempArr = []; //用于以风机id 聚合dataArr
+        if (wtRes.data?.length) {
+          for (const data of wtRes.data) {
+            if (tempArr.length) {
+              const findIndex = tempArr.findIndex((o) => o.wtId === data.wtId);
+              if (findIndex !== -1) {
+                if (!tempArr[findIndex].children) {
+                  tempArr[findIndex].children = [];
+                }
+                tempArr[findIndex].children.push({
+                  ...data,
+                  id: id,
+                  filter: data.filter === 0 ? "是" : "否",
+                });
+                id++;
+              } else {
+                tempArr.push({
+                  ...data,
+                  id: id,
+                  filter: data.filter === 0 ? "是" : "否",
+                });
+                id++;
+              }
+            } else {
+              tempArr.push({
+                ...data,
+                id: id,
+                filter: data.filter === 0 ? "是" : "否",
+              });
+              id++;
+            }
+          }
+          wtDialog.value = true;
+          nextTick(() => {
+            wtTab.value = "table";
+            wtData.value = tempArr;
+          });
+        }
+      }
+    }
+  }
+};
+const funChartArea = () => {
+  if (seriesData.value?.length) {
+    if (!isChartArea.value) {
+      // 请求一下
+      seriesData.value[0] = {
+        ...seriesData.value[0],
+        markLine: {
+          symbol: "none",
+          label: {
+            show: false,
+          },
+          lineStyle: {
+            color: "rgba(96,174,255, 1)",
+          },
+          data: [
+            {
+              xAxis: 3,
+              valueIndex: 0,
+            },
+            {
+              xAxis: 5,
+              valueIndex: 0,
+            },
+            {
+              xAxis: 10,
+              valueIndex: 0,
+            },
+            {
+              xAxis: 12,
+              valueIndex: 0,
+            },
+            {
+              xAxis: 25,
+              valueIndex: 0,
+            },
+          ],
+        },
+        markArea: {
+          label: {
+            fontSize: 13,
+            color: "#ccc",
+            fontWeight: "normal",
+            fontFamily: "Arial",
+          },
+          itemStyle: {
+            color: "rgba(236,245,255, 0)",
+          },
+          emphasis: {
+            itemStyle: {
+              color: "rgba(96,174,255, 0.5)",
+            },
+          },
+          data: [
+            [
+              {
+                name: `3~5m 偏差率: ${markDot.pcl5}%`,
+                xAxis: 3,
+              },
+              {
+                xAxis: 5,
+              },
+            ],
+            [
+              {
+                name: `5~10m 偏差率: ${markDot.pcl10}%`,
+                xAxis: 5,
+              },
+              {
+                xAxis: 10,
+              },
+            ],
+            [
+              {
+                name: `10~12m 偏差率: ${markDot.pcl12}%`,
+                xAxis: 10,
+              },
+              {
+                xAxis: 12,
+              },
+            ],
+            [
+              {
+                name: `12~25m 偏差率: ${markDot.pcl25}%`,
+                xAxis: 12,
+              },
+              {
+                xAxis: 25,
+              },
+            ],
+          ],
+        },
+      };
+      isChartArea.value = true;
+    } else {
+      seriesData.value[0] = {
+        ...seriesData.value[0],
+        markLine: null,
+        markArea: null,
+      };
+      isChartArea.value = false;
+    }
+  }
+};
+const handleClick = (val) => {
+  wtTab.value = val.props.name;
+  if (wtTab.value === "problem") {
+    faultDataFn();
+  } else if (wtTab.value === "warning") {
+    warnDataFn();
+  }
+};
+
+//故障数据
+const faultDataFn = async () => {
+  let params = {
+    only: dataRadom.value,
+    table: "alarmWt",
+  };
+  const faultRes = await getWindFittingAlarms(params);
+  faultData.value = faultRes.data;
+};
+//预警数据
+const warnDataFn = async () => {
+  let params = {
+    only: dataRadom.value,
+    table: "alarmCt",
+  };
+  const warnRes = await getWindFittingAlarms(params);
+  warnData.value = warnRes.data;
+};
+
+/**dialog 数据 */
+const wtDialog = ref(false);
+const wtData = ref([]);
+const faultData = ref([]);
+const warnData = ref([]);
+const wtTab = ref("table");
+// 随机数
+const dataRadom = ref(null);
+/**tab  */
+const activeTab = ref("1");
+/**created */
+funGetTree();
+funGetProcessTree();
+/**mounted */
+
+/**activated */
+onActivated(() => {
+  funGetTree();
+  funGetProcessTree();
+});
+</script>
+<template>
+  <div class="container-wrapper">
+    <search-cop @submit="funSubmit"> </search-cop>
+    <el-dialog v-model="wtDialog" draggable>
+      <template #title>
+        <div class="dialog-title">
+          <div class="title">风机功率点位</div>
+        </div>
+      </template>
+      <el-tabs v-model="wtTab" @tab-click="handleClick">
+        <el-tab-pane label="数据" name="table">
+          <el-table :data="wtData" row-key="id" :max-height="550">
+            <el-table-column property="wtId" align="center" label="风机" />
+            <el-table-column
+              property="time"
+              sortable
+              :width="160"
+              align="center"
+              label="时间"
+            />
+            <el-table-column
+              property="speed"
+              sortable
+              align="center"
+              label="风速(m/s)"
+            />
+            <el-table-column
+              property="power"
+              sortable
+              align="center"
+              label="功率(kW)"
+            />
+            <el-table-column
+              property="rr"
+              sortable
+              align="center"
+              label="转速"
+            />
+            <el-table-column
+              property="filter"
+              sortable
+              align="center"
+              label="是否有用点"
+            />
+          </el-table>
+        </el-tab-pane>
+        <el-tab-pane label="故障" name="problem">
+          <el-table :data="faultData" row-key="id" :max-height="550">
+            <el-table-column property="deviceId" align="center" label="风机" />
+            <el-table-column
+              property="ts"
+              sortable
+              :width="160"
+              align="center"
+              label="时间"
+            >
+              <template v-slot="scope">
+                {{ new Date(scope.row.ts).formatDate("yyyy-MM-dd hh:mm:ss") }}
+              </template>
+            </el-table-column>
+            <el-table-column
+              property="characteristic"
+              sortable
+              align="center"
+              label="特性"
+            />
+            <el-table-column
+              property="components"
+              sortable
+              align="center"
+              label="部件"
+            />
+            <el-table-column
+              property="description"
+              sortable
+              align="center"
+              label="描述"
+            />
+          </el-table>
+        </el-tab-pane>
+        <el-tab-pane label="预警" name="warning">
+          <el-table :data="warnData" row-key="id" :max-height="550">
+            <el-table-column property="deviceId" align="center" label="风机" />
+            <el-table-column
+              property="ts"
+              sortable
+              :width="160"
+              align="center"
+              label="时间"
+            >
+              <template v-slot="scope">
+                {{ new Date(scope.row.ts).formatDate("yyyy-MM-dd hh:mm:ss") }}
+              </template>
+            </el-table-column>
+            <el-table-column
+              property="characteristic"
+              sortable
+              align="center"
+              label="特性"
+            />
+            <el-table-column
+              property="components"
+              sortable
+              align="center"
+              label="部件"
+            />
+            <el-table-column
+              property="description"
+              sortable
+              align="center"
+              label="描述"
+            />
+          </el-table>
+        </el-tab-pane>
+      </el-tabs>
+    </el-dialog>
+    <div class="power-data-wrapper card-shadow wrapper">
+      <div class="card-title">数据展示</div>
+      <div class="data-wrapper">
+        <div class="data-left-wrapper">
+          <tree-cop
+            class="tree-height"
+            :data="treeData"
+            @checkChange="funTreeCheckChange"
+            :show-checkbox="true"
+            @currentChange="funCurrentChange"
+            @refresh="funGetTree"
+          />
+          <tree-cop
+            class="tree-height"
+            :dropdownMenu="['export', 'delete', 'save']"
+            :data="processTreeData"
+            @currentChange="funProcessCurrentChange"
+            @refresh="funGetProcessTree"
+          />
+        </div>
+        <div class="data-excel-wrapper">
+          <excel-cop
+            class="excel-height"
+            :checkIds="excelCheckIds"
+            :showCheckbox="excelCheckboxShow"
+            :data="excelList"
+            @excelChange="funExcelChange"
+            @checkChange="funExcelCheckChange"
+          />
+          <excel-cop
+            :currentIds="currentId"
+            class="excel-height"
+            :data="excelFitList"
+            @excelChange="funExcelChange"
+          />
+        </div>
+
+        <div class="data-table-wrapper card-shadow">
+          <SubmitBtn
+            class="submit-btn"
+            desc="区域划分"
+            v-if="activeTab === '2' && excelType === 'fitting'"
+            @click="funChartArea"
+          ></SubmitBtn>
+
+          <el-tabs v-model="activeTab" class="data-table-tabs">
+            <el-tab-pane label="表格数据" name="1"> </el-tab-pane>
+            <el-tab-pane
+              label="图表展示"
+              name="2"
+              v-if="excelType === 'fitting'"
+            >
+            </el-tab-pane>
+
+            <table-cop
+              v-show="activeTab === '1'"
+              :showShadow="false"
+              :data="tableData"
+              :loading="tableLoading"
+              :column="tableColumn"
+              :tableId="tableShowId"
+              :tableName="tableName"
+            />
+            <div v-show="activeTab === '2'" class="data-chart-wrapper">
+              <CurrentScatterChart
+                ref="chartRef"
+                width="100%"
+                height="100%"
+                :chartTitle="
+                  avgObj.title +
+                  '&nbsp;&nbsp;' +
+                  '平均Cp值:' +
+                  avgObj.cpavg +
+                  '; 静风频率:' +
+                  avgObj.frequency +
+                  '%; 曲线偏差率:' +
+                  avgObj.pcratio +
+                  '%'
+                "
+                :xAxisData="xAxisData"
+                :yAxisData="{ splitLine: { show: false } }"
+                :seriesData="seriesData"
+                :showLegend="true"
+                :brushSelected="!isChartArea"
+                :dataSet="dataSet"
+                @getSelected="funChartSelect"
+              />
+            </div>
+          </el-tabs>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<style lang="less" scoped>
+.el-tabs ::v-deep {
+  height: calc(100% - 22px);
+  .el-tabs__item {
+    color: #b3b3b3;
+    padding: 0 12px;
+  }
+  .el-tabs__nav-wrap::after {
+    background-color: #14221f;
+  }
+  .el-tabs__active-bar {
+    background-color: #05bb4c;
+  }
+  .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>

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

@@ -0,0 +1,12 @@
+<template>
+  <router-view />
+</template>
+
+<script>
+export default {
+  name: "windAnalyse", // 风电分析
+};
+</script>
+
+<style scoped lang="less">
+</style>

+ 190 - 0
src/views/powerGenerating/windAnalyse/rateAnalysis/components/chart.vue

@@ -0,0 +1,190 @@
+<script setup>
+import util from "@tools/util";
+import chartTheme from "./../rateAnalysis.json";
+import {
+  ref,
+  toRaw,
+  computed,
+  onMounted,
+  watch,
+  nextTick,
+  defineProps,
+} from "vue";
+import { useStore } from "vuex";
+import * as echarts from "echarts";
+const chartId = "chart-" + util.newGUID(); //chartId
+const chartIns = ref(null); //chart 实例
+const props = defineProps({
+  xAxis: {
+    type: Object,
+    required: true,
+    default: () => ({}),
+  },
+  series: {
+    type: Array,
+    required: true,
+  },
+  height: {
+    type: String,
+    required: false,
+    default: "500px",
+  },
+  width: {
+    type: String,
+    required: false,
+    default: "500px",
+  },
+  title: {
+    type: String,
+    required: false,
+  },
+  subtext: {
+    type: String,
+    required: false,
+  },
+  // 是否为dialog中单表
+  isDiaAlone: {
+    type: Boolean,
+    default: false,
+  },
+  // 是否含雷达图
+  isRadar: {
+    type: Boolean,
+    default: false,
+  },
+});
+
+/**定义option */
+const option = computed({
+  get() {
+    let radar = null;
+    if (props.isRadar && props.xAxis?.data?.length) {
+      radar = {
+        radius: "70%",
+        center: ["60%", "50%"],
+        indicator: props.xAxis.data.map((o) => {
+          return {
+            text: "",
+            max: 1000,
+          };
+        }),
+        splitArea: {
+          show: false,
+        },
+        splitLine: {
+          show: false,
+        },
+      };
+    }
+    return {
+      title: {
+        text: props.title || "",
+        subtext: props.subtext || "",
+        textStyle: {
+          color: "#b2bcbf",
+        },
+        subtextStyle: {
+          color: "#b2bcbf",
+        },
+        top: 10,
+        left: props.isDiaAlone ? "22%" : "5%",
+      },
+      angleAxis: props.xAxis || {},
+      radiusAxis: {},
+      polar: {
+        radius: "70%",
+        center: ["60%", "50%"],
+      },
+      radar: radar,
+      tooltip: {
+        formatter: (params) => {
+          return params.componentSubType === "radar"
+            ? `${params.marker}${params.seriesName}`
+            : `${params.marker}${params.seriesName}m<br/>${
+                params.value > 1 ? "频次:" + params.value : ""
+              }`;
+        },
+        confine: true,
+      },
+      series: props.series || [],
+      legend: {
+        show: true,
+        orient: "vertical",
+        left: props.isDiaAlone ? "22%" : "5%",
+        itemWidth: 16,
+        itemHeight: 10,
+        textStyle: {
+          fontSize: util.vh(10),
+        },
+        top: "middle",
+        data: [
+          "0-2.5",
+          "2.5-5",
+          "5-7.5",
+          "7.5-10",
+          "10-12.5",
+          "12.5-15",
+          "15-17.5",
+          "17.5-20",
+          "20-22.5",
+          "22.5-25",
+          "25-inf",
+        ],
+      },
+    };
+  },
+  set(val) {},
+});
+watch(
+  () => option,
+  (newVal, oldVal) => {
+    if (chartIns.value) {
+      const echartIns = toRaw(chartIns.value);
+      echartIns.setOption(newVal.value);
+    }
+  },
+  { deep: true }
+);
+
+watch([() => props.width, () => props.height], (newVal, oldVal) => {
+  if (chartIns.value) {
+    const echartIns = toRaw(chartIns.value);
+    nextTick(() => echartIns.resize());
+  }
+});
+const store = useStore();
+const collapse = computed({
+  get() {
+    return store.state.collapse;
+  },
+  set(val) {},
+});
+watch(collapse, (val) => {
+  if (chartIns.value) {
+    setTimeout(() => {
+      chartIns.value?.resize();
+    }, 300);
+  }
+});
+
+onMounted(() => {
+  nextTick(() => {
+    echarts.registerTheme("chartTheme", chartTheme);
+    const echartIns = echarts.init(
+      document.getElementById(chartId),
+      "chartTheme"
+    );
+    chartIns.value = echartIns;
+    echartIns.setOption(option.value);
+    window.addEventListener("resize", () => {
+      echartIns.resize();
+    });
+  });
+});
+</script>
+<template>
+  <div
+    :id="chartId"
+    :style="{ height: props.height, width: props.width }"
+  ></div>
+</template>

+ 228 - 0
src/views/powerGenerating/windAnalyse/rateAnalysis/components/lineChart.vue

@@ -0,0 +1,228 @@
+<script setup>
+import util from "@tools/util";
+import chartTheme from "./../rateAnalysis.json";
+import {
+  ref,
+  toRaw,
+  computed,
+  onMounted,
+  watch,
+  nextTick,
+  defineEmits,
+  defineProps,
+} from "vue";
+import { useStore } from "vuex";
+import * as echarts from "echarts";
+const chartId = "chart-" + util.newGUID(); //chartId
+const chartIns = ref(null); //chart 实例
+const emits = defineEmits(["getSelected"]);
+const props = defineProps({
+  xAxis: {
+    type: Object,
+    required: true,
+    default: () => ({}),
+  },
+  yAxis: {
+    type: Array,
+    required: false,
+  },
+  series: {
+    type: Array,
+    required: true,
+  },
+  dataset: {
+    type: Array,
+    required: false,
+    default: () => [],
+  },
+  height: {
+    type: String,
+    required: false,
+    default: "500px",
+  },
+  width: {
+    type: String,
+    required: false,
+    default: "500px",
+  },
+  title: {
+    type: String,
+    required: false,
+  },
+  subtext: {
+    type: String,
+    required: false,
+  },
+  brush: {
+    type: Boolean,
+    required: false,
+    default: false,
+  },
+});
+
+/**定义option */
+const option = computed({
+  get() {
+    return {
+      color: [
+        "#FF8700",
+        "#0098d980",
+        "#626c91",
+        "#a0a7e6",
+        "#c4ebad",
+        "#96dee8",
+      ],
+      title: {
+        text: props.title || "",
+        subtext: props.subtext || "",
+        top: 6,
+        left: "5%",
+        textStyle: {
+          color: "#b2bcbf",
+        },
+        subtextStyle: {
+          color: "#b2bcbf",
+        },
+      },
+      xAxis: props.xAxis || {},
+      yAxis: props.yAxis || {},
+      brush: {
+        seriesIndex: [1],
+        yAxisIndex: 0,
+        transformable: true,
+        throttleType: "debounce",
+        throttleDelay: 1000,
+        removeOnClick: true,
+        brushType: props.brush ? "polygon" : false,
+        brushMode: "multiple",
+        brushStyle: {
+          borderWidth: 1,
+          borderColor: "#ff2424",
+        },
+      },
+      toolbox: {
+        show: props.brush,
+      },
+      tooltip: {
+        confine: true,
+        axisPointer: {
+          type: "cross",
+        },
+      },
+      dataset: props.dataset || [],
+      series: props.series || [],
+      legend: {
+        right: "120",
+        top: "5",
+        itemWidth: 6,
+      },
+      grid: {
+        top: 80,
+        left: 40,
+        right: 40,
+        bottom: 40,
+      },
+      dataZoom: [
+        {
+          type: "inside", //图表下方的伸缩条
+          show: false, //是否显示
+          realtime: true, //拖动时,是否实时更新系列的视图
+          start: 0, //伸缩条开始位置(1-100),可以随时更改
+          end: 100, //伸缩条结束位置(1-100),可以随时更改
+        },
+        {
+          type: "slider", //图表下方的伸缩条
+          show: false, //是否显示
+          realtime: true, //拖动时,是否实时更新系列的视图
+          start: 0, //伸缩条开始位置(1-100),可以随时更改
+          end: 100, //伸缩条结束位置(1-100),可以随时更改
+        },
+      ],
+    };
+  },
+  set(val) {},
+});
+watch(
+  () => option,
+  (newVal, oldVal) => {
+    if (chartIns.value) {
+      const echartIns = toRaw(chartIns.value);
+      echartIns.setOption(toRaw(newVal.value));
+    }
+  },
+  { deep: true }
+);
+watch([() => props.width, () => props.height], (newVal, oldVal) => {
+  if (chartIns.value) {
+    const echartIns = toRaw(chartIns.value);
+    nextTick(() => echartIns.resize());
+  }
+});
+const store = useStore();
+const collapse = computed({
+  get() {
+    return store.state.collapse;
+  },
+  set(val) {},
+});
+watch(collapse, (val) => {
+  if (chartIns.value) {
+    setTimeout(() => {
+      chartIns.value?.resize();
+    }, 300);
+  }
+});
+const funBrushChange = (flag) => {
+  const echartIns = toRaw(chartIns.value);
+  echartIns.dispatchAction({
+    type: "takeGlobalCursor",
+    // 如果想变为“可刷选状态”,必须设置。不设置则会关闭“可刷选状态”。
+    key: "brush",
+    brushOption: {
+      seriesIndex: [1],
+      yAxisIndex: 0,
+      transformable: true,
+      throttleType: "debounce",
+      throttleDelay: 1000,
+      removeOnClick: true,
+      brushType: flag ? "polygon" : false,
+      brushMode: "multiple",
+      brushStyle: {
+        borderWidth: 1,
+        color: "rgba(255,36,36,0.2)",
+        borderColor: "#ff2424",
+      },
+    },
+  });
+  echartIns.off("brushSelected");
+  echartIns.on("brushSelected", (params) => {
+    emits("getSelected", params.batch || []);
+  });
+};
+watch(
+  () => props.brush,
+  (newVal, oldVal) => funBrushChange(newVal)
+);
+
+onMounted(() => {
+  nextTick(() => {
+    echarts.registerTheme("chartTheme", chartTheme);
+    const echartIns = echarts.init(
+      document.getElementById(chartId),
+      "chartTheme"
+    );
+    chartIns.value = echartIns;
+    echartIns.setOption(option.value);
+    funBrushChange(props.brush);
+    window.addEventListener("resize", () => {
+      echartIns.resize();
+    });
+  });
+});
+</script>
+<template>
+  <div
+    :id="chartId"
+    :style="{ height: props.height, width: props.width }"
+  ></div>
+</template>

+ 182 - 0
src/views/powerGenerating/windAnalyse/rateAnalysis/components/scatterSingleChart.vue

@@ -0,0 +1,182 @@
+<script setup name="scatterSingleChart">
+import util from "@tools/util";
+import chartTheme from "./../rateAnalysis.json";
+import {
+  ref,
+  toRaw,
+  computed,
+  onMounted,
+  watch,
+  nextTick,
+  defineProps,
+} from "vue";
+import { useStore } from "vuex";
+import * as echarts from "echarts";
+const chartId = "chart-" + util.newGUID(); //chartId
+const chartIns = ref(null); //chart 实例
+const props = defineProps({
+  xAxis: {
+    type: Array,
+    required: true,
+    default: () => [],
+  },
+  yAxis: {
+    type: Array,
+    required: true,
+    default: () => [],
+  },
+  series: {
+    type: Array,
+    required: false,
+    default: () => [],
+  },
+  height: {
+    type: String,
+    required: false,
+    default: "500px",
+  },
+  title: {
+    type: String,
+    required: false,
+    default: "",
+  },
+  subtext: {
+    type: String,
+    required: false,
+    default: "",
+  },
+  width: {
+    type: String,
+    required: false,
+    default: "500px",
+  },
+});
+
+/**定义option */
+const option = computed({
+  get() {
+    return {
+      tooltip: {
+        position: "top",
+        formatter: function (params) {
+          if (params.componentType === "markLine") {
+            return params.name;
+          } else {
+            return (
+              "偏航:" +
+              params.value[0] +
+              "度<br/ >风速:" +
+              params.value[1] +
+              "m/s"
+            );
+          }
+        },
+      },
+      title: {
+        text: props.title || "",
+        subtext: props.subtext || "",
+        top: 6,
+        left: "5%",
+        textStyle: {
+          color: "#b2bcbf",
+        },
+        subtextStyle: {
+          color: "#b2bcbf",
+        },
+      },
+      grid: {
+        top: 80,
+        left: 40,
+        right: 40,
+        bottom: 40,
+      },
+      xAxis: props.xAxis || [],
+      //  {
+      // 	type: 'category',
+      // 	data: props.xAxis || [],
+      // 	boundaryGap: false,
+      // 	splitLine: {
+      // 		show: true
+      // 	},
+      // 	axisLine: {
+      // 		show: false
+      // 	}
+      // },
+      yAxis: props.yAxis || [],
+      // {
+      // 	type: 'category',
+      // 	data: props.yAxis,
+      // 	axisLine: {
+      // 		show: false
+      // 	}
+      // },
+      series: props.series || [],
+      // [
+      // 	{
+      // 		name: 'Punch Card',
+      // 		type: 'scatter',
+      // 		symbolSize: function (val) {
+      // 			return val[2] * 2;
+      // 		},
+      // 		data: props.data,
+      // 		animationDelay: function (idx) {
+      // 			return idx * 5;
+      // 		}
+      // 	}
+      // ]
+    };
+  },
+  set(val) {},
+});
+watch(
+  () => option,
+  (newVal, oldVal) => {
+    if (chartIns.value) {
+      // console.log(newVal)
+      const echartIns = toRaw(chartIns.value);
+      echartIns.setOption(toRaw(newVal.value));
+    }
+  },
+  { deep: true }
+);
+watch([() => props.width, () => props.height], (newVal, oldVal) => {
+  if (chartIns.value) {
+    const echartIns = toRaw(chartIns.value);
+    nextTick(() => echartIns.resize());
+  }
+});
+const store = useStore();
+const collapse = computed({
+  get() {
+    return store.state.collapse;
+  },
+  set(val) {},
+});
+watch(collapse, (val) => {
+  if (chartIns.value) {
+    setTimeout(() => {
+      chartIns.value?.resize();
+    }, 300);
+  }
+});
+onMounted(() => {
+  nextTick(() => {
+    echarts.registerTheme("chartTheme", chartTheme);
+    const echartIns = echarts.init(
+      document.getElementById(chartId),
+      "chartTheme"
+    );
+    chartIns.value = echartIns;
+    echartIns.setOption(option.value);
+    window.addEventListener("resize", () => {
+      echartIns.resize();
+    });
+  });
+});
+</script>
+<template>
+  <div
+    :id="chartId"
+    :style="{ height: props.height, width: props.width }"
+  ></div>
+</template>

File diff ditekan karena terlalu besar
+ 1026 - 0
src/views/powerGenerating/windAnalyse/rateAnalysis/index.vue


+ 399 - 0
src/views/powerGenerating/windAnalyse/rateAnalysis/rateAnalysis.json

@@ -0,0 +1,399 @@
+
+{
+	"color": [
+			"#626c91",
+			"#96dee8",
+			"#3fb1e3",
+			"#6be6c1",
+			"#a0a7e6",
+			"#c4ebad",
+			"rgb(126,254,123)",
+			"rgb(254,200,10)",
+			"rgb(224,103,2)",
+			"rgb(242,8,1)",
+			"rgb(127,0,1)"
+
+	],
+	"backgroundColor": "rgba(252,252,252,0)",
+	"textStyle": {},
+	"title": {
+			"textStyle": {
+					"color": "#000"
+			},
+			"subtextStyle": {
+					"color": "#000"
+			}
+	},
+	"line": {
+			"itemStyle": {
+					"borderWidth": "2"
+			},
+			"lineStyle": {
+					"width": "2"
+			},
+			"symbolSize": "8",
+			"symbol": "emptyCircle",
+			"smooth": true
+	},
+	"radar": {
+			"itemStyle": {
+					"borderWidth": "2"
+			},
+			"lineStyle": {
+					"width": "2"
+			},
+			"symbolSize": "8",
+			"symbol": "emptyCircle",
+			"smooth": false
+	},
+	"bar": {
+			"itemStyle": {
+					"barBorderWidth": 0,
+					"barBorderColor": "#ccc"
+			}
+	},
+	"pie": {
+			"itemStyle": {
+					"borderWidth": 0,
+					"borderColor": "#ccc"
+			}
+	},
+	"scatter": {
+			"itemStyle": {
+					"borderWidth": 0,
+					"borderColor": "#ccc"
+			}
+	},
+	"boxplot": {
+			"itemStyle": {
+					"borderWidth": 0,
+					"borderColor": "#ccc"
+			}
+	},
+	"parallel": {
+			"itemStyle": {
+					"borderWidth": 0,
+					"borderColor": "#ccc"
+			}
+	},
+	"sankey": {
+			"itemStyle": {
+					"borderWidth": 0,
+					"borderColor": "#ccc"
+			}
+	},
+	"funnel": {
+			"itemStyle": {
+					"borderWidth": 0,
+					"borderColor": "#ccc"
+			}
+	},
+	"gauge": {
+			"itemStyle": {
+					"borderWidth": 0,
+					"borderColor": "#ccc"
+			}
+	},
+	"candlestick": {
+			"itemStyle": {
+					"color": "#e6a0d2",
+					"color0": "transparent",
+					"borderColor": "#e6a0d2",
+					"borderColor0": "#3fb1e3",
+					"borderWidth": "2"
+			}
+	},
+	"graph": {
+			"itemStyle": {
+					"borderWidth": 0,
+					"borderColor": "#ccc"
+			},
+			"lineStyle": {
+					"width": "1",
+					"color": "#cccccc"
+			},
+			"symbolSize": "8",
+			"symbol": "emptyCircle",
+			"smooth": false,
+			"color": [
+					"#3fb1e3",
+					"#6be6c1",
+					"#626c91",
+					"#a0a7e6",
+					"#c4ebad",
+					"#96dee8"
+			],
+			"label": {
+					"color": "#ffffff"
+			}
+	},
+	"map": {
+			"itemStyle": {
+					"areaColor": "#eeeeee",
+					"borderColor": "#aaaaaa",
+					"borderWidth": 0.5
+			},
+			"label": {
+					"color": "#ffffff"
+			},
+			"emphasis": {
+					"itemStyle": {
+							"areaColor": "rgba(63,177,227,0.25)",
+							"borderColor": "#3fb1e3",
+							"borderWidth": 1
+					},
+					"label": {
+							"color": "#3fb1e3"
+					}
+			}
+	},
+	"geo": {
+			"itemStyle": {
+					"areaColor": "#eeeeee",
+					"borderColor": "#aaaaaa",
+					"borderWidth": 0.5
+			},
+			"label": {
+					"color": "#ffffff"
+			},
+			"emphasis": {
+					"itemStyle": {
+							"areaColor": "rgba(63,177,227,0.25)",
+							"borderColor": "#3fb1e3",
+							"borderWidth": 1
+					},
+					"label": {
+							"color": "#3fb1e3"
+					}
+			}
+	},
+	"categoryAxis": {
+			"axisLine": {
+					"show": true,
+					"lineStyle": {
+							"color": "#838383"
+					}
+			},
+			"axisTick": {
+					"show": false,
+					"lineStyle": {
+							"color": "#838383"
+					}
+			},
+			"axisLabel": {
+					"show": true,
+					"color": "#838383"
+			},
+			"splitLine": {
+					"show": true,
+					"lineStyle": {
+							"color": [
+									"#838383"
+							]
+					}
+			},
+			"splitArea": {
+					"show": false,
+					"areaStyle": {
+							"color": [
+									"rgba(250,250,250,0.05)",
+									"rgba(200,200,200,0.02)"
+							]
+					}
+			}
+	},
+	"valueAxis": {
+			"axisLine": {
+					"show": true,
+					"lineStyle": {
+							"color": "#838383"
+					}
+			},
+			"axisTick": {
+					"show": false,
+					"lineStyle": {
+							"color": "#838383"
+					}
+			},
+			"axisLabel": {
+					"show": true,
+					"color": "#838383",
+					"fontSize": 10
+			},
+			"splitLine": {
+					"show": true,
+					"lineStyle": {
+							"color": [
+									"#838383"
+							]
+					}
+			},
+			"splitArea": {
+					"show": false,
+					"areaStyle": {
+							"color": [
+									"rgba(250,250,250,0.05)",
+									"rgba(200,200,200,0.02)"
+							]
+					}
+			}
+	},
+	"logAxis": {
+			"axisLine": {
+					"show": true,
+					"lineStyle": {
+							"color": "#cccccc"
+					}
+			},
+			"axisTick": {
+					"show": false,
+					"lineStyle": {
+							"color": "#333"
+					}
+			},
+			"axisLabel": {
+					"show": true,
+					"color": "#999999"
+			},
+			"splitLine": {
+					"show": true,
+					"lineStyle": {
+							"color": [
+									"#eeeeee"
+							]
+					}
+			},
+			"splitArea": {
+					"show": false,
+					"areaStyle": {
+							"color": [
+									"rgba(250,250,250,0.05)",
+									"rgba(200,200,200,0.02)"
+							]
+					}
+			}
+	},
+	"timeAxis": {
+			"axisLine": {
+					"show": true,
+					"lineStyle": {
+							"color": "#cccccc"
+					}
+			},
+			"axisTick": {
+					"show": false,
+					"lineStyle": {
+							"color": "#333"
+					}
+			},
+			"axisLabel": {
+					"show": true,
+					"color": "#999999"
+			},
+			"splitLine": {
+					"show": true,
+					"lineStyle": {
+							"color": [
+									"#eeeeee"
+							]
+					}
+			},
+			"splitArea": {
+					"show": false,
+					"areaStyle": {
+							"color": [
+									"rgba(250,250,250,0.05)",
+									"rgba(200,200,200,0.02)"
+							]
+					}
+			}
+	},
+	"toolbox": {
+			"iconStyle": {
+					"borderColor": "#999999"
+			},
+			"emphasis": {
+					"iconStyle": {
+							"borderColor": "#666666"
+					}
+			}
+	},
+	"legend": {
+			"textStyle": {
+					"color": "#999999"
+			}
+	},
+	"tooltip": {
+			"axisPointer": {
+					"lineStyle": {
+							"color": "#cccccc",
+							"width": 1
+					},
+					"crossStyle": {
+							"color": "#cccccc",
+							"width": 1
+					}
+			}
+	},
+	"timeline": {
+			"lineStyle": {
+					"color": "#626c91",
+					"width": 1
+			},
+			"itemStyle": {
+					"color": "#626c91",
+					"borderWidth": 1
+			},
+			"controlStyle": {
+					"color": "#626c91",
+					"borderColor": "#626c91",
+					"borderWidth": 0.5
+			},
+			"checkpointStyle": {
+					"color": "#3fb1e3",
+					"borderColor": "#3fb1e3"
+			},
+			"label": {
+					"color": "#626c91"
+			},
+			"emphasis": {
+					"itemStyle": {
+							"color": "#626c91"
+					},
+					"controlStyle": {
+							"color": "#626c91",
+							"borderColor": "#626c91",
+							"borderWidth": 0.5
+					},
+					"label": {
+							"color": "#626c91"
+					}
+			}
+	},
+	"visualMap": {
+			"color": [
+					"#2a99c9",
+					"#afe8ff"
+			]
+	},
+	"dataZoom": {
+			"backgroundColor": "rgba(255,255,255,0)",
+			"dataBackgroundColor": "rgba(222,222,222,1)",
+			"fillerColor": "rgba(114,230,212,0.25)",
+			"handleColor": "#cccccc",
+			"handleSize": "100%",
+			"textStyle": {
+					"color": "#999999"
+			}
+	},
+	"markPoint": {
+			"label": {
+					"color": "#ffffff"
+			},
+			"emphasis": {
+					"label": {
+							"color": "#ffffff"
+					}
+			}
+	}
+}