Quellcode durchsuchen

新增短期展示

xusl vor 2 Jahren
Ursprung
Commit
a83590ea6b

+ 68 - 0
backend/src/main/java/com/jiayue/ssi/controller/ForecastPowerShortTermController.java

@@ -0,0 +1,68 @@
+package com.jiayue.ssi.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.jiayue.ssi.constant.CustomException;
+import com.jiayue.ssi.entity.*;
+import com.jiayue.ssi.service.*;
+import com.jiayue.ssi.util.*;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 短期接口
+ *
+ * @author xsl
+ * @since 2023/03/13
+ */
+@RestController
+@RequestMapping("/forecastPowerShortTermController")
+@Slf4j
+public class ForecastPowerShortTermController {
+    @Autowired
+    ForecastPowerShortTermService forecastPowerShortTermService;
+
+    /**
+     * 获取短期分页信息
+     *
+     * @return 用户信息
+     */
+    @GetMapping(value = "/getAll")
+    public ResponseVO getAll(Integer currentPage, Integer pageSize, String startTime, String endTime) throws CustomException {
+        try {
+            QueryWrapper<ForecastPowerShortTerm> wrapper = new QueryWrapper<>();
+            if (StringUtils.isNotEmpty(startTime)) {
+                wrapper.ge("forecast_time", startTime);
+            }
+            if (StringUtils.isNotEmpty(endTime)) {
+                wrapper.le("forecast_time", endTime);
+            }
+            wrapper.orderByAsc("forecast_time");
+            Page<ForecastPowerShortTerm> result = forecastPowerShortTermService.page(new Page<>(currentPage, pageSize), wrapper);
+            return ResponseVO.success(result);
+        } catch (Exception e) {
+            throw new CustomException("获取短期列表异常", e);
+        }
+    }
+    /**
+     * 按时间查询实时预测短期
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @return 结果集
+     */
+    @GetMapping(value = "/getDraw")
+    public ResponseVO getDraw(String startTime, String endTime) throws CustomException{
+        Map<String,Object> map = new HashMap<>();
+        try{
+            map = forecastPowerShortTermService.findByForecastTimeBetween(Long.parseLong(startTime),Long.parseLong(endTime));
+            return ResponseVO.success(map);
+        } catch (Exception e) {
+            throw new CustomException("获取短期图形数据异常", e);
+        }
+    }
+}

+ 6 - 1
backend/src/main/java/com/jiayue/ssi/mapper/ForecastPowerShortTermMapper.java

@@ -3,6 +3,10 @@ package com.jiayue.ssi.mapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.jiayue.ssi.entity.ForecastPowerShortTerm;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
 
 /**
  *  短期Mapper
@@ -12,5 +16,6 @@ import org.apache.ibatis.annotations.Mapper;
  */
 @Mapper
 public interface ForecastPowerShortTermMapper extends BaseMapper<ForecastPowerShortTerm> {
-
+    @Select("SELECT t.* FROM t_forecast_power_short_term t WHERE t.forecast_time>= #{startTime} and t.forecast_time<= #{endTime} order by t.forecast_time")
+    public List<ForecastPowerShortTerm> findByForecastTimeBetween(@Param("startTime") Long startTime, @Param("endTime") Long endTime);
 }

+ 9 - 0
backend/src/main/java/com/jiayue/ssi/service/ForecastPowerShortTermService.java

@@ -7,6 +7,7 @@ import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
+import java.util.Map;
 
 
 /**
@@ -23,4 +24,12 @@ public interface ForecastPowerShortTermService extends IService<ForecastPowerSho
      * @param listDq
      */
     void deleteBetweenAndGenTime(Long startTime, Long endTime, List<ForecastPowerShortTerm> listDq);
+    /**
+     * 按时间查询实时预测短期
+     *
+     * @param startTime 开始时间
+     * @param endTime   结束时间
+     * @return 结果集
+     */
+    Map<String, Object> findByForecastTimeBetween(Long startTime, Long endTime);
 }

+ 48 - 1
backend/src/main/java/com/jiayue/ssi/service/impl/ForecastPowerShortTermServiceImpl.java

@@ -2,15 +2,20 @@ package com.jiayue.ssi.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.jiayue.ssi.entity.ElectricField;
 import com.jiayue.ssi.entity.ForecastPowerShortTerm;
 import com.jiayue.ssi.mapper.ForecastPowerShortTermMapper;
+import com.jiayue.ssi.service.ElectricFieldService;
 import com.jiayue.ssi.service.ForecastPowerShortTermService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
-import java.util.List;
+
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.stream.Collectors;
 
 
 @Service
@@ -18,6 +23,8 @@ import java.util.List;
 public class ForecastPowerShortTermServiceImpl extends ServiceImpl<ForecastPowerShortTermMapper, ForecastPowerShortTerm> implements ForecastPowerShortTermService {
   @Autowired
   ForecastPowerShortTermMapper forecastPowerShortTermMapper;
+  @Autowired
+  ElectricFieldService electricFieldService;
 
   /**
    * 按时间段删除数据
@@ -36,5 +43,45 @@ public class ForecastPowerShortTermServiceImpl extends ServiceImpl<ForecastPower
     //保存短期数据
     this.saveBatch(listDq);
   }
+  /**
+   * 按时间查询实时预测短期
+   *
+   * @param startTime 开始时间
+   * @param endTime   结束时间
+   * @return 结果集
+   */
+  @Override
+  public Map<String, Object> findByForecastTimeBetween(Long startTime, Long endTime) {
+    Map<String, Object> map = new HashMap<>();
+    ElectricField electricField = electricFieldService.getOne(null);
+    map.put("cap",electricField.getCapacity());
+    List<ForecastPowerShortTerm> list = new ArrayList<>();
+    list = forecastPowerShortTermMapper.findByForecastTimeBetween(startTime, endTime);
+    list.sort(Comparator.comparing(ForecastPowerShortTerm::getForecastTime));
 
+    long startTimeLong = startTime;
+    long endTimeLong = endTime;
+    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+    long timeStep = 900000L;
+    if (startTimeLong % timeStep != 0) {
+      startTimeLong = startTimeLong - (startTimeLong % timeStep)+timeStep;
+    }
+    List<Float> datas = new ArrayList<>();
+    List<String> times = new ArrayList<>();
+
+    for (long i = startTimeLong; i < endTimeLong; i = i + timeStep) {
+      long finalI = i;
+      List<ForecastPowerShortTerm> p = list.stream().filter(t -> t.getForecastTime() == finalI).collect(Collectors.toList());
+      if (p != null && p.size() > 0) {
+        datas.add(p.get(0).getFpValue().floatValue());
+      } else {
+        datas.add(null);
+      }
+      String timeFormat = sdf.format(new Date(i));
+      times.add(timeFormat);
+    }
+    map.put("times", times);
+    map.put("datas", datas);
+    return map;
+  }
 }

+ 83 - 0
ui/src/components/Charts/mixins/resize.js

@@ -0,0 +1,83 @@
+import { debounce } from 'lodash'
+export default {
+  data() {
+    return {
+      $_sidebarElm: null,
+      $_itemElm1:null,
+      $_itemElm2:null,
+      $_itemElm3:null,
+      $_itemElm4:null,
+      $_windTowerItemElm1:null,
+      $_windTowerItemElm2:null,
+      $_windTowerItemElm3:null,
+      $_tabsClass:null,
+      $_resizeHandler: null,
+
+    }
+  },
+  mounted() {
+    this.initListener()
+  },
+  activated() {
+    if (!this.$_resizeHandler) {
+      // avoid duplication init
+      this.initListener()
+    }
+    // when keep-alive chart activated, auto resize
+    this.resize()
+  },
+  beforeDestroy() {
+    this.destroyListener()
+  },
+  deactivated() {
+    this.destroyListener()
+  },
+  methods: {
+    // use $_ for mixins properties
+    // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
+    $_sidebarResizeHandler(e) {
+      if (e.propertyName === 'width') {
+        this.$_resizeHandler()
+      }
+    },
+
+    initListener() {
+      this.$_resizeHandler = debounce(() => {
+        this.resize()
+      }, 100)
+      window.addEventListener('resize', this.$_resizeHandler)
+
+      this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]
+      this.$_itemElm1 = document.getElementsByClassName('item')[0]
+      this.$_itemElm2 = document.getElementsByClassName('item')[1]
+      this.$_itemElm3 = document.getElementsByClassName('item')[2]
+
+      this.$_windTowerItemElm1 = document.getElementsByClassName('windTowerItem')[0]
+      this.$_windTowerItemElm2 = document.getElementsByClassName('windTowerItem')[1]
+      this.$_windTowerItemElm3 = document.getElementsByClassName('windTowerItem')[2]
+
+
+      this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)
+      this.$_itemElm1 && this.$_itemElm1.addEventListener('transitionend', this.$_sidebarResizeHandler)
+      this.$_itemElm2 && this.$_itemElm2.addEventListener('transitionend', this.$_sidebarResizeHandler)
+      this.$_itemElm3 && this.$_itemElm3.addEventListener('transitionend', this.$_sidebarResizeHandler)
+
+      this.$_windTowerItemElm1 && this.$_windTowerItemElm1.addEventListener('transitionend', this.$_sidebarResizeHandler)
+      this.$_windTowerItemElm2 && this.$_windTowerItemElm2.addEventListener('transitionend', this.$_sidebarResizeHandler)
+      this.$_windTowerItemElm3 && this.$_windTowerItemElm3.addEventListener('transitionend', this.$_sidebarResizeHandler)
+
+
+
+    },
+    destroyListener() {
+      window.removeEventListener('resize', this.$_resizeHandler)
+      this.$_resizeHandler = null
+
+      this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
+    },
+    resize() {
+      const { chart } = this
+      chart && chart.resize()
+    }
+  }
+}

+ 6 - 1
ui/src/main.js

@@ -12,17 +12,22 @@ import plugins from './plugins' // plugins
 import App from './App'
 import store from './store'
 import router, {resetRouter} from './router'
-
+import echarts from 'echarts'
 import '@/icons' // icon
 import '@/permission' // permission control
 // import axios from 'axios'
 import VXETable from 'vxe-table'
 import 'vxe-table/lib/index.css'
 import service from './utils/request'
+import XEUtils from 'xe-utils'
 import {removeToken} from './utils/auth'
 // import { resetForm} from "@/utils/index"
 import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi"
 Vue.prototype.$moment = moment
+Vue.prototype.$echarts = echarts
+Vue.prototype.$XEUtils = XEUtils
+import _ from 'lodash'
+Vue.prototype._ = _
 Vue.prototype.$axios = service
 Vue.use(VXETable)
 Vue.use(plugins)

+ 129 - 0
ui/src/views/bizManager/forecastPowerShortTerm/charts/index.vue

@@ -0,0 +1,129 @@
+<template>
+  <div style="width: 100%;height: 100%" >
+    <div id="fpcharts"></div>
+  </div>
+</template>
+
+<script>
+import resize from '../../../../components/Charts/mixins/resize'
+import * as echarts from 'echarts';
+export default {
+  mixins: [resize],
+  watch: {
+    drawData:{
+      handler(newValue, oldValue) {
+        this.draw(newValue.times, newValue.datas,newValue.cap)
+      },
+      deep: true
+    },
+    resizeKey:function(newQuestion, oldQuestion){
+      if(this.chart !=null){
+        this.chart.resize();
+      }
+    }
+  },
+  props: {
+    drawData:{
+      type:Object,
+    },
+    resizeKey:{
+      type:Number
+    }
+  },
+  data() {
+    return {
+      chart: null,
+    }
+  },
+  mounted() {
+  },
+  beforeDestroy() {
+    if (!this.chart) {
+      return
+    }
+    this.chart.dispose()
+    this.chart = null
+  },
+  methods: {
+    draw(timeaxis,realpower,cap) {
+      console.log(timeaxis.length)
+      this.chart = echarts.init(document.getElementById('fpcharts'))
+      var option ={
+        title: {
+          top: 20,
+          text: '短期预测实时查询',
+          textStyle: {
+            fontWeight: 'normal',
+            fontSize: 16,
+          },
+          left: '1%'
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            lineStyle: {
+              color: '#57617B'
+            }
+          }
+        },
+        legend: {
+          top: 20,
+          icon: 'rect',
+          itemWidth: 14,
+          itemHeight: 5,
+          itemGap: 13,
+          data: ['实时短期预测'],
+          right: '4%',
+          textStyle: {
+            fontSize: 12,
+          }
+        },
+        dataZoom: [{
+          show: true,
+          realtime: true,
+
+          left:"15%",
+          right:"15%",
+          textStyle:{
+            color:"#ffffff"
+          }
+        }, {
+          type: 'inside'
+        }],
+        grid: {
+          top: 100,
+          left: '2%',
+          right: '2%',
+          bottom: '10%',
+          containLabel: true
+        },
+        xAxis: {
+          type: 'category',
+          data: timeaxis
+        },
+        yAxis: {
+          type: 'value',
+          name: '(MW)',
+        },
+        series: [
+          {
+            name: '实时短期预测',
+            data: realpower,
+            type: 'line',
+            smooth: true
+          }
+        ]
+      }
+      option.yAxis[0] = cap
+      this.chart.setOption(option,true)
+    },
+
+  }
+}
+</script>
+<style scoped>
+#fpcharts{
+  width: 100%;
+  height:calc(80vh - 50px);
+}
+</style>

+ 319 - 0
ui/src/views/bizManager/forecastPowerShortTerm/index.vue

@@ -0,0 +1,319 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="24">
+      <!--用户数据-->
+      <el-col :span="24" :xs="24">
+        <el-form ref="queryForm" size="small" :inline="true" label-width="68px">
+          <el-form-item label="起始时间" prop="startTime">
+            <el-date-picker
+              v-model="startTime"
+              :clearable="false"
+              type="datetime"
+              value-format="timestamp"
+              placeholder="选择日期">
+            </el-date-picker>
+          </el-form-item>
+          <el-form-item label="截止时间" prop="endTime">
+            <el-date-picker
+              v-model="endTime"
+              :clearable="false"
+              type="datetime"
+              value-format="timestamp"
+              placeholder="选择日期">
+            </el-date-picker>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="el-icon-search" size="mini" @click="dateQuery">查询</el-button>
+          </el-form-item>
+        </el-form>
+      </el-col>
+    </el-row>
+
+    <div class="content">
+      <el-tabs type="card" v-model="activeName" @tab-click="Byresize">
+        <el-tab-pane label="图表" name="first">
+          <chart :drawData = this.drawData :resizeKey=this.resizeKey  />
+        </el-tab-pane>
+        <el-tab-pane label="表格" name="second">
+          <div class="tableContent">
+            <vxe-table
+              id="fstTable"
+              ref="fstRef"
+              border
+              export-config
+              beforeExportMethod=""
+              :auto-resize="true"
+              highlight-hover-row
+              max-height="90%"
+              align="center"
+              :data="tableData">
+              <vxe-table-column  field="forecastTime" title="预测时间" :formatter="dateFormat" width="250" sortable min-width="250"></vxe-table-column>
+              <vxe-table-column field="fpValue" title="预测功率" min-width="60" ></vxe-table-column>
+            </vxe-table>
+            <vxe-pager
+              v-show="showTable"
+              perfect
+              :current-page.sync="currentPage"
+              :page-size.sync="pageSize"
+              :total="total"
+              :page-sizes="[10,50,100]"
+              :layouts="['PrevJump', 'PrevPage','JumpNumber', 'NextPage', 'NextJump', 'Sizes', 'FullJump', 'Total']"
+              @page-change="handlePageChange"
+            >
+            </vxe-pager>
+          </div>
+          <!-- <Table height="100%" width="100%" :queryTime=this.queryTime @sendLoading="getLoadingFormTable"></Table>-->
+        </el-tab-pane>
+      </el-tabs>
+    </div>
+  </div>
+</template>
+
+<script>
+import Chart from './charts'
+import resize from '../../../components/Charts/mixins/resize'
+import moment from "moment";
+export default {
+  name: 'nwp',
+  components: { Chart},
+  mixins: [resize],
+  data(){
+    return{
+      showTable: true,
+      chart: null,
+      queryStartTime:'',
+      queryEndTime:'',
+      startTime:new Date(new Date().toLocaleDateString()).getTime()+ 60 * 60 * 24 * 1000,
+      endTime:new Date(new Date().toLocaleDateString()).getTime() + 60 * 60 * 24 * 1000*4-1,
+      loading:false,
+      drawLoading:true,
+      tableLoading:true,
+      resizeKey:1,
+      activeName: 'first',
+      drawData:{datas:[],times:[]},
+      tableData:[],
+      total:0,
+      sortOrder:'asc',
+      pageSize: 10,
+      currentPage: 1,
+      showToolBar:false,
+      /*menuKey:1,
+      isRenderingTime : new Date().getTime()*/
+
+    }
+  },
+  created () {
+  },
+  mounted() {
+    this.queryStartTime = this.startTime
+    this.queryEndTime = this.endTime
+    this.getDraw()
+    this.getTable()
+
+  },
+  methods:{
+    getDraw(){
+      this.drawLoading = true
+      var searchParams = {
+        startTime: this.queryStartTime,
+        endTime: this.queryEndTime
+      }
+      this.$axios.get('/forecastPowerShortTermController/getDraw',{params: searchParams}).then((res) => {
+        this.drawData = res.data
+      }).catch((error) => {
+        this.$message.error('查询实时预测短期echarts出错' + error)
+      })
+    },
+    getTable(){
+      var searchParams = {
+        currentPage: this.currentPage,
+        pageSize: this.pageSize,
+        startTime: this.queryStartTime,
+        endTime: this.queryEndTime
+      }
+      this.$axios.get('/forecastPowerShortTermController/getAll',
+        {params: searchParams}).then((res) => {
+        this.tableData = res.data.records
+        this.total = res.data.total
+      }).catch((error) => {
+        // this.$message.error(error)
+      })
+    },
+    handlePageChange ({ currentPage, pageSize }) {
+      this.currentPage = currentPage
+      this.pageSize = pageSize
+      this.startTime = this.queryStartTime
+      this.endTime = this.queryEndTime
+      this.loading = true
+      this.getTable();
+    },
+    dateFormat({ cellValue, row, column }) {
+      return this.$XEUtils.toDateString(cellValue, 'yyyy-MM-dd HH:mm:ss')
+    },
+    enumToWord({ cellValue, row, column }) {
+      if(cellValue == "E1"){
+        return "云端模型"
+      }
+      if(cellValue == 'E2'){
+        return "物理模型"
+      }
+      if(cellValue == 'E3'){
+        return "统计模型"
+      }
+      if(cellValue == 'E4'){
+        return "补录数据"
+      }
+      if(cellValue == 'E5'){
+        return "差值模型"
+      }
+    },
+    dateMoment({ cellValue, row, column }) {
+      return moment(cellValue).format('YYYY-MM-DD HH:mm:ss')
+    },
+    sortChangeEvent ({ column, property, order }) {
+      if(order == null){
+        order = 'asc'
+      }
+      this.currentPage = 1
+      this.sortOrder = order
+      this.loading = true
+      this.getTable()
+    },
+
+    checkColumnMethod ({ column }) {
+      if (column.property === 'preTime') {
+        return false
+      }
+      return true
+    },
+    dateQuery(){
+      this.loading = true
+      if(this.endTime<=this.startTime){
+        this.$message.error("开始时间不能大于结束时间")
+        // this.startTime = this.queryStartTime
+        // this.endTime = this.queryEndTime
+        this.loading = false
+        return
+      }
+      if(this.endTime-this.startTime> 60 * 60 * 24 * 1000*3){
+        // this.startTime = this.queryStartTime
+        // this.endTime = this.queryEndTime
+        this.$message.error("只能最多查询3天的数据哦")
+        this.loading = false
+        return
+      }
+      this.queryStartTime = this.startTime
+      this.queryEndTime = this.endTime
+      this.getDraw(this.queryStartTime,this.queryEndTime)
+      this.getTable()
+    },
+    Byresize(tab){
+      if(tab.name =='first'){
+        this.resizeKey++
+        this.showToolBar = false
+      }else{
+        this.showToolBar = true
+      }
+
+    },
+  }
+}
+</script>
+
+<style scoped>
+.chart-container{
+  position:relative;
+  width:100%;
+  height:calc(100vh - 50px);
+}
+
+.filter{
+  position:relative;
+  display:flex;
+  padding:20px 0 10px 15px;
+  font-size:12px;
+  line-height:11px;
+  color:white;
+}
+
+input{
+  background:transparent;
+  border:none;
+  color:white;
+}
+
+.timeText{
+  opacity:0.69;
+  padding-right:7px;
+  font-size:14px;
+}
+
+.startTime{
+  display:inline-block;
+}
+
+.endTime{
+  display:inline-block;
+  padding-left:42px;
+}
+
+
+.timeQuery{
+  background:transparent;
+}
+
+.filter{
+  width: 100%;background-color: transparent;height: 10%
+}
+.filter >>> input{
+  background:transparent;
+  border:none;
+  color:white;
+}
+.content{
+  width: 100%;
+  background-color: transparent;
+  height: 90%;
+  padding-left: 5px;
+  padding-right: 5px;
+}
+
+
+
+.tableContent{
+  width: 100%;
+  height:calc(80vh - 50px);
+}
+.tableContent >>> td{
+  border:1px solid #ffffff;
+}
+
+.rtPageturning >>> button,
+.rtPageturning >>> span,
+.rtPageturning >>> input,
+.rtPageturning >>> .vxe-pager--btn-wrapper li{
+  background-color: transparent !important;
+  color: #ffffff !important;
+  border: 1px solid #ffffff;
+}
+.rtPageturning >>> span{
+  border:none
+}
+.rtPageturning >>> .vxe-pager--wrapper .vxe-pager--btn-wrapper li:not(.disabled).is--active {
+  background-color: #9f9fa0 !important;
+}
+.toolbar{
+  position:absolute;right:0px;
+}
+.toolbar >>> .vxe-button.type--button.is--circle {
+  padding: 0 .5em;
+  min-width: 34px;
+  border-radius: 10%;
+  border: none;
+  background: transparent;
+  color: white;
+}
+
+
+</style>
+

+ 0 - 2
ui/src/views/sysManager/userManager/index.vue

@@ -11,7 +11,6 @@
               placeholder="请输入用户账号"
               clearable
               style="width: 240px"
-              @keyup.enter.native="handleQuery"
             />
           </el-form-item>
           <el-form-item label="手机号码" prop="phonenumber">
@@ -21,7 +20,6 @@
               placeholder="请输入手机号码"
               clearable
               style="width: 240px"
-              @keyup.enter.native="handleQuery"
             />
           </el-form-item>
           <el-form-item label="用户状态" prop="status">