Explorar o código

feat: 解决预测效果分页不在联动echart效果

sunxiao hai 7 meses
pai
achega
b3712a1fa9

+ 7 - 7
src/views/business/comparison/components/LineEchart.vue

@@ -25,12 +25,12 @@ const activeIndex = ref(0);
 const activeItem = computed(() => props.category[activeIndex.value]);
 
 watch(() => props.echartParams, () => {
-  formatData();
+  createEchart();
 })
 
-const formatData = async (type) => {
-  const { timeBegin, timeEnd, category } = props.echartParams || {};
-  
+const createEchart = async (type) => {
+  const { timeBegin, timeEnd, category, pageSize, pageNum } = props.echartParams || {};
+
   echart.showLoading();
   
   if ( (category || category == null) && type != 'slef') {
@@ -91,7 +91,7 @@ const formatData = async (type) => {
 
 const changeActive = (i) => {
   activeIndex.value = i;
-  formatData('slef');
+  createEchart('slef');
 }
 
 const legendselectchanged = () => {
@@ -129,7 +129,7 @@ onMounted(async () => {
 
   window.addEventListener('resize', windowResize);
 
-  formatData();
+  createEchart();
 
   legendselectchanged();
 })
@@ -139,7 +139,7 @@ onUnmounted(() => {
 })
 
 defineExpose({
-  initEchart: formatData
+  createEchart
 })
 
 </script>

+ 215 - 76
src/views/business/comparison/index.vue

@@ -1,26 +1,15 @@
 <template>
   <div class="page-container">
-    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px" class="search-form_container">
+    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px"
+      class="search-form_container">
       <el-form-item label="预测时间" prop="daterange">
-        <el-date-picker
-          :editable="false"
-          value-format="YYYY/MM/DD HH:mm:ss"
-          v-model="queryParams.daterange"
-          type="datetimerange"
-          range-separator="To"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
-        />
+        <el-date-picker :editable="false" value-format="YYYY/MM/DD HH:mm:ss" v-model="queryParams.daterange"
+          type="datetimerange" range-separator="To" start-placeholder="开始日期" end-placeholder="结束日期" />
       </el-form-item>
       <el-form-item label="预测类型" prop="category">
         <el-select v-model="queryParams.category">
           <el-option value="" label="全部"></el-option>
-          <el-option
-            :key="item"
-            :value="item.value"
-            :label="item.label"
-            v-for="item in category"
-          ></el-option>
+          <el-option :key="item" :value="item.value" :label="item.label" v-for="item in category"></el-option>
         </el-select>
       </el-form-item>
       <el-form-item>
@@ -30,58 +19,55 @@
     </el-form>
 
     <el-card shadow="never" class="card-chart_container">
-      <LineEchart :category="category" :echartParams="echartParams" ref="refEchart"></LineEchart>
-      </el-card>
+      <div class="echart-container">
+        <h4 class="title">{{ activeItem.label }} 预测趋势图</h4>
+        <div ref="echartRefDom" class="echart-inner"></div>
+        <ul class="legend-inner space-x-[40px]">
+          <li v-for="item, index in category" :key="index" :class="['legend-item', { active: activeIndex == index }]"
+            @click="changeActive(index)">{{ item.label }}</li>
+        </ul>
+      </div>
+    </el-card>
     <el-card shadow="never" body-class="card-table_container">
       <el-row :gutter="10" class="mb8">
         <el-col :span="1.5">
-          <el-button
-            type="warning"
-            plain
-            icon="download"
-            size="small"
-            @click="handleExport"
-          >导出</el-button>
+          <el-button type="warning" plain icon="download" size="small" @click="handleExport">导出</el-button>
         </el-col>
-        <right-toolbar @update:showSearch="(v) => showSearch = v" @queryTable="getList" :showSearch="showSearch"></right-toolbar>
+        <right-toolbar @update:showSearch="(v) => showSearch = v" @queryTable="getList"
+          :showSearch="showSearch"></right-toolbar>
       </el-row>
 
       <el-table v-loading="loading" :data="comparisonList" @selection-change="handleSelectionChange">
-        <el-table-column label="时间" align="center" prop="remark" width="120"/>
-        <el-table-column label="预测类型" align="center" prop="category" width="120"/> 
-        <el-table-column label="预测1时间" align="center" prop="forecastTimeOne" width="120"/>
-        <el-table-column label="实际1小时值" align="center" prop="realOne" width="120"/>
-        <el-table-column label="TFT预测1小时值" align="center" prop="hsForecastOne" width="120"/>
-        <el-table-column label="TFT预测1小时误差值" align="center" prop="hsOneSubtract" width="150"/>
-        <el-table-column label="TFT预测1小时百分比误差" align="center" prop="hsErrorRateOneStr" width="180"/>
-        <el-table-column label="LSTM预测1小时值" align="center" prop="yyForecastOne" width="140"/>
-        <el-table-column label="LSTM预测1误差值" align="center" prop="yyOneSubtract" width="150"/>
-        <el-table-column label="LSTM预测1小时百分比误差" align="center" prop="yyErrorRateOneStr" width="200"/>
-        <el-table-column label="预测2时间" align="center" prop="forecastTimeTwo" width="120"/>
-        <el-table-column label="实际2小时值" align="center" prop="realTwo" width="120"/>
-        <el-table-column label="TFT预测2小时值" align="center" prop="hsForecastTwo" width="120"/>
-        <el-table-column label="TFT预测2误差值" align="center" prop="hsTwoSubtract" width="150"/>
-        <el-table-column label="TFT预测2小时百分比误差" align="center" prop="hsErrorRateTwoStr" width="180"/>
-        <el-table-column label="LSTM预测2小时值" align="center" prop="yyForecastTwo" width="140"/>
-        <el-table-column label="LSTM预测2误差值" align="center" prop="yyTwoSubtract" width="140"/>
-        <el-table-column label="LSTM预测2小时百分比误差" align="center" prop="yyErrorRateTwoStr" width="200"/>
-        <el-table-column label="预测3时间" align="center" prop="forecastTimeThree" width="120"/>
-        <el-table-column label="实际3小时值" align="center" prop="realThree" width="120"/>
-        <el-table-column label="TFT预测3小时值" align="center" prop="hsForecastThree" width="120"/>
-        <el-table-column label="TFT预测3误差值" align="center" prop="hsThreeSubtract" width="120"/>
-        <el-table-column label="TFT预测3小时百分比误差" align="center" prop="hsErrorRateThreeStr" width="180"/>
-        <el-table-column label="LSTM预测3小时值" align="center" prop="yyForecastThree" width="140"/>
-        <el-table-column label="LSTM预测3误差值" align="center" prop="yyThreeSubtract" width="140"/>
-        <el-table-column label="LSTM预测3小时百分比误差" align="center" prop="yyErrorRateThreeStr" width="190"/>
+        <el-table-column label="时间" align="center" prop="remark" width="120" />
+        <el-table-column label="预测类型" align="center" prop="category" width="120" />
+        <el-table-column label="预测1时间" align="center" prop="forecastTimeOne" width="120" />
+        <el-table-column label="实际1小时值" align="center" prop="realOne" width="120" />
+        <el-table-column label="TFT预测1小时值" align="center" prop="hsForecastOne" width="120" />
+        <el-table-column label="TFT预测1小时误差值" align="center" prop="hsOneSubtract" width="150" />
+        <el-table-column label="TFT预测1小时百分比误差" align="center" prop="hsErrorRateOneStr" width="180" />
+        <el-table-column label="LSTM预测1小时值" align="center" prop="yyForecastOne" width="140" />
+        <el-table-column label="LSTM预测1误差值" align="center" prop="yyOneSubtract" width="150" />
+        <el-table-column label="LSTM预测1小时百分比误差" align="center" prop="yyErrorRateOneStr" width="200" />
+        <el-table-column label="预测2时间" align="center" prop="forecastTimeTwo" width="120" />
+        <el-table-column label="实际2小时值" align="center" prop="realTwo" width="120" />
+        <el-table-column label="TFT预测2小时值" align="center" prop="hsForecastTwo" width="120" />
+        <el-table-column label="TFT预测2误差值" align="center" prop="hsTwoSubtract" width="150" />
+        <el-table-column label="TFT预测2小时百分比误差" align="center" prop="hsErrorRateTwoStr" width="180" />
+        <el-table-column label="LSTM预测2小时值" align="center" prop="yyForecastTwo" width="140" />
+        <el-table-column label="LSTM预测2误差值" align="center" prop="yyTwoSubtract" width="140" />
+        <el-table-column label="LSTM预测2小时百分比误差" align="center" prop="yyErrorRateTwoStr" width="200" />
+        <el-table-column label="预测3时间" align="center" prop="forecastTimeThree" width="120" />
+        <el-table-column label="实际3小时值" align="center" prop="realThree" width="120" />
+        <el-table-column label="TFT预测3小时值" align="center" prop="hsForecastThree" width="120" />
+        <el-table-column label="TFT预测3误差值" align="center" prop="hsThreeSubtract" width="120" />
+        <el-table-column label="TFT预测3小时百分比误差" align="center" prop="hsErrorRateThreeStr" width="180" />
+        <el-table-column label="LSTM预测3小时值" align="center" prop="yyForecastThree" width="140" />
+        <el-table-column label="LSTM预测3误差值" align="center" prop="yyThreeSubtract" width="140" />
+        <el-table-column label="LSTM预测3小时百分比误差" align="center" prop="yyErrorRateThreeStr" width="190" />
       </el-table>
-  
-      <pagination
-        v-show="total > 0"
-        :total="total"
-        v-model:page="queryParams.pageNum"
-        v-model:limit="queryParams.pageSize"
-        @pagination="getList"
-      />
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize" @pagination="getList('slef')" />
     </el-card>
     <!-- 添加或修改信义预测对比对话框 -->
     <el-dialog :title="title" v-model="open" width="900px" append-to-body>
@@ -164,11 +150,18 @@
 <script>
 import { listComparison, getComparison, delComparison, addComparison, updateComparison } from "@/api/business/comparison";
 import dayjs from "dayjs";
-import LineEchart from "./components/LineEchart";
+import * as echarts from 'echarts';
+// import LineEchart from "./components/LineEchart";
+import { getEchartData } from "@/api/business/comparison";
+import { getEchartLineOption } from "./components/echartConfig";
+
+let echart = null;
+let lastClickTime = new Date();
+
 export default {
-  components: { LineEchart },
+  // components: { LineEchart },
   name: "Comparison",
-    data() {
+  data() {
     return {
       // 遮罩层
       loading: true,
@@ -210,30 +203,149 @@ export default {
       rules: {
       },
       echartParams: {},
-      refEchart: null,
+      echartRef: null,
+      activeIndex: 0,
+      echartRefDom: null
     };
   },
+  computed: {
+    activeItem() {
+      return this.category[this.activeIndex]
+    }
+  },
   created() {
     this.getList();
   },
+  mounted() {
+    echart = echarts.init(this.$refs['echartRefDom']);
+
+    this.createEchart();
+
+    this.legendselectchanged();
+
+    window.addEventListener('resize', this.windowResize);
+
+  },
+  unmounted() {
+    window.addEventListener('resize', this.windowResize);
+  },
   methods: {
+    windowResize() {
+      echart.resize()
+    },
+    async createEchart(type) {
+      const { timeBegin, timeEnd, category } = this.echartParams || {};
+
+      echart.showLoading();
+
+      if ((category || category == null) && type != 'slef') {
+        const index = this.category.findIndex(item => item.value == category);
+        this.activeIndex = index === -1 ? 0 : index;
+      }
+      
+      
+      const data = await getEchartData({ category: this.activeItem.value, timeBegin, timeEnd });
+    
+
+      const xAxisData = [];
+      const realOneList = [];
+      const realTwoList = [];
+      const realThreeList = [];
+      const hsForecastOneList = [];
+      const hsForecastTwoList = [];
+      const hsForecastThreeList = [];
+      const yyForecastOneList = [];
+      const yyForecastTwoList = [];
+      const yyForecastThreeList = [];
+
+      data.map(item => {
+        const {
+          remark,
+          realOne, realTwo, realThree,
+          hsForecastOne, hsForecastTwo, hsForecastThree,
+          yyForecastOne, yyForecastTwo, yyForecastThree
+        } = item;
+        xAxisData.push(remark);
+        realOneList.push(realOne);
+        realTwoList.push(realTwo);
+        realThreeList.push(realThree);
+        hsForecastOneList.push(hsForecastOne);
+        hsForecastTwoList.push(hsForecastTwo);
+        hsForecastThreeList.push(hsForecastThree);
+        yyForecastOneList.push(yyForecastOne);
+        yyForecastTwoList.push(yyForecastTwo);
+        yyForecastThreeList.push(yyForecastThree);
+      })
+
+      const echartData = [
+        { name: 'Real_1', val: realOneList },
+        { name: 'Real_2', val: realTwoList },
+        { name: 'Real_3', val: realThreeList },
+        { name: 'TFTpre_1', val: hsForecastOneList },
+        { name: 'TFTpre_2', val: hsForecastTwoList },
+        { name: 'TFTpre_3', val: hsForecastThreeList },
+        { name: 'LSTMpre_1', val: yyForecastOneList },
+        { name: 'LSTMpre_2', val: yyForecastTwoList },
+        { name: 'LSTMpre_3', val: yyForecastThreeList }
+      ]
+
+      const option = getEchartLineOption({ xAxisData, echartData })
+
+      echart.setOption(option);
+
+      echart.hideLoading();
+    },
+
+    changeActive(i) {
+      this.activeIndex = i;
+      this.createEchart("slef");
+    },
+
+    legendselectchanged() {
+      echart.on('legendselectchanged', function (params) {
+
+        let currentTime = new Date().getTime();
+        let timeDiff = currentTime - lastClickTime;
+
+        const legendData = echart.getOption().series;
+
+        if (timeDiff < 300) {
+          for (var i = 0; i < legendData.length; i++) {
+            if (params.name == legendData[i].name) {
+              echart.dispatchAction({
+                type: 'legendSelect',
+                name: legendData[i].name
+              });
+            } else {
+              echart.dispatchAction({
+                type: 'legendUnSelect',
+                name: legendData[i].name
+              });
+            }
+          }
+        }
+        lastClickTime = currentTime;
+      });
+    },
+
     /** 查询信义预测对比列表 */
     getList() {
+
       this.loading = true;
-      const [ begin, end ] = this.queryParams.daterange || [];
+      const [begin, end] = this.queryParams.daterange || [];
       let timeBegin = '';
       let timeEnd = '';
-   
-      if ( begin ) {
+
+      if (begin) {
         const endHour = dayjs(end).format('HH');
         timeBegin = dayjs(begin).format('YYYY/MM/DD HH');
         timeEnd = endHour === '00' ? dayjs(end).format('YYYY/MM/DD') + ' 24' : dayjs(end).format('YYYY/MM/DD HH')
       }
-      
+
       const params = { ...this.queryParams, timeBegin, timeEnd };
 
       delete params.daterange;
-      
+
       this.echartParams = params
 
       listComparison(params).then(response => {
@@ -245,7 +357,7 @@ export default {
           cod: 'COD',
           ss: 'SS',
         }
-        this.comparisonList = response.rows.map(item => ({...item, category: whiteList[item.category]}));
+        this.comparisonList = response.rows.map(item => ({ ...item, category: whiteList[item.category] }));
         this.total = response.total;
         this.loading = false;
       });
@@ -292,6 +404,7 @@ export default {
     handleQuery() {
       this.queryParams.pageNum = 1;
       this.getList();
+      this.createEchart();
     },
     /** 重置按钮操作 */
     resetQuery() {
@@ -301,7 +414,7 @@ export default {
     // 多选框选中数据
     handleSelectionChange(selection) {
       this.ids = selection.map(item => item.id)
-      this.single = selection.length!==1
+      this.single = selection.length !== 1
       this.multiple = !selection.length
     },
     /** 新增按钮操作 */
@@ -343,12 +456,12 @@ export default {
     /** 删除按钮操作 */
     handleDelete(row) {
       const ids = row.id || this.ids;
-      this.$modal.confirm('是否确认删除信义预测对比编号为"' + ids + '"的数据项?').then(function() {
+      this.$modal.confirm('是否确认删除信义预测对比编号为"' + ids + '"的数据项?').then(function () {
         return delComparison(ids);
       }).then(() => {
         this.getList();
         this.$modal.msgSuccess("删除成功");
-      }).catch(() => {});
+      }).catch(() => { });
     },
     /** 导出按钮操作 */
     handleExport() {
@@ -360,8 +473,34 @@ export default {
 };
 </script>
 
-<style scoped lang="scss">
-.echart-wrap {
-  // height: 420px;
+
+<style lang="scss" scoped>
+.echart-container {
+  .title {
+    padding: 10px 0 20px 0;
+    text-align: center;
+    font-weight: bold;
+  }
+
+  .echart-inner {
+    height: 280px;
+  }
+
+  .legend-inner {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    padding-top: 30px;
+    font-size: 14px;
+    color: #999;
+
+    .legend-item {
+      cursor: pointer;
+    }
+
+    .active {
+      color: #409eff;
+    }
+  }
 }
 </style>