Prechádzať zdrojové kódy

feat: 预测对比增加ecahrt, 头像显示修改, 预测类型select调整

sunxiao 7 mesiacov pred
rodič
commit
c1781226fc

+ 1 - 1
src/api/business/assistant.js

@@ -41,4 +41,4 @@ export function delAssistant(id) {
     url: '/business/assistant/' + id,
     method: 'delete'
   })
-}
+}

+ 9 - 0
src/api/business/comparison.js

@@ -42,3 +42,12 @@ export function delComparison(id) {
     method: 'delete'
   })
 }
+
+// 查询echart数据
+export function getEchartData(query) {
+  return request({
+    url: '/business/comparison/echartsList',
+    method: 'get',
+    params: query
+  })
+}

+ 146 - 128
src/assets/styles/ruoyi.scss

@@ -1,133 +1,145 @@
- /**
+/**
  * 通用css样式布局处理
  * Copyright (c) 2019 ruoyi
  */
 
- /** 基础通用 **/
+/** 基础通用 **/
 .pt5 {
-	padding-top: 5px;
+  padding-top: 5px;
 }
 .pr5 {
-	padding-right: 5px;
+  padding-right: 5px;
 }
 .pb5 {
-	padding-bottom: 5px;
+  padding-bottom: 5px;
 }
 .mt5 {
-	margin-top: 5px;
+  margin-top: 5px;
 }
 .mr5 {
-	margin-right: 5px;
+  margin-right: 5px;
 }
 .mb5 {
-	margin-bottom: 5px;
+  margin-bottom: 5px;
 }
 .mb8 {
-	margin-bottom: 8px;
+  margin-bottom: 8px;
 }
 .ml5 {
-	margin-left: 5px;
+  margin-left: 5px;
 }
 .mt10 {
-	margin-top: 10px;
+  margin-top: 10px;
 }
 .mr10 {
-	margin-right: 10px;
+  margin-right: 10px;
 }
 .mb10 {
-	margin-bottom: 10px;
+  margin-bottom: 10px;
 }
 .ml10 {
-	margin-left: 10px;
+  margin-left: 10px;
 }
 .mt20 {
-	margin-top: 20px;
+  margin-top: 20px;
 }
 .mr20 {
-	margin-right: 20px;
+  margin-right: 20px;
 }
 .mb20 {
-	margin-bottom: 20px;
+  margin-bottom: 20px;
 }
 .ml20 {
-	margin-left: 20px;
-}
-
-.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
-	font-family: inherit;
-	font-weight: 500;
-	line-height: 1.1;
-	color: inherit;
+  margin-left: 20px;
+}
+
+.h1,
+.h2,
+.h3,
+.h4,
+.h5,
+.h6,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+  font-family: inherit;
+  font-weight: 500;
+  line-height: 1.1;
+  color: inherit;
 }
 
 .el-form .el-form-item__label {
-	font-weight: 700;
+  font-weight: 700;
 }
 .el-dialog:not(.is-fullscreen) {
-	margin-top: 6vh !important;
+  margin-top: 6vh !important;
 }
 
 .el-dialog.scrollbar .el-dialog__body {
-	overflow: auto;
-	overflow-x: hidden;
-	max-height: 70vh;
-	padding: 10px 20px 0;
+  overflow: auto;
+  overflow-x: hidden;
+  max-height: 70vh;
+  padding: 10px 20px 0;
 }
 
 .el-table {
-	.el-table__header-wrapper, .el-table__fixed-header-wrapper {
-		th {
-			word-break: break-word;
-			background-color: #f8f8f9 !important;
-			color: #515a6e;
-			height: 40px !important;
-			font-size: 13px;
-		}
-	}
-	.el-table__body-wrapper {
-		.el-button [class*="el-icon-"] + span {
-			margin-left: 1px;
-		}
-	}
+  .el-table__header-wrapper,
+  .el-table__fixed-header-wrapper {
+    th {
+      word-break: break-word;
+      background-color: #f8f8f9 !important;
+      color: #515a6e;
+      height: 40px !important;
+      font-size: 13px;
+    }
+  }
+  .el-table__body-wrapper {
+    .el-button [class*="el-icon-"] + span {
+      margin-left: 1px;
+    }
+  }
 }
 
 /** 表单布局 **/
 .form-header {
-    font-size:15px;
-	color:#6379bb;
-	border-bottom:1px solid #ddd;
-	margin:8px 10px 25px 10px;
-	padding-bottom:5px
+  font-size: 15px;
+  color: #6379bb;
+  border-bottom: 1px solid #ddd;
+  margin: 8px 10px 25px 10px;
+  padding-bottom: 5px;
 }
 
 /** 表格布局 **/
 .pagination-container {
-    // position: relative;
-    // height: 25px;
-    // margin-bottom: 10px;
-    // margin-top: 15px;
-    // padding: 10px 20px !important;
+  // position: relative;
+  // height: 25px;
+  // margin-bottom: 10px;
+  // margin-top: 15px;
+  // padding: 10px 20px !important;
 }
 
 .el-dialog .pagination-container {
-    position: static !important;
+  position: static !important;
 }
 
 /* tree border */
 .tree-border {
-    margin-top: 5px;
-    border: 1px solid #e5e6e7;
-    background: #FFFFFF none;
-    border-radius:4px;
-    width: 100%;
+  margin-top: 5px;
+  border: 1px solid #e5e6e7;
+  background: #ffffff none;
+  border-radius: 4px;
+  width: 100%;
 }
 
 .pagination-container .el-pagination {
-	// right: 0;
-	// position: absolute;
+  // right: 0;
+  // position: absolute;
 }
 
-@media ( max-width : 768px) {
+@media (max-width: 768px) {
   .pagination-container .el-pagination > .el-pagination__jump {
     display: none !important;
   }
@@ -137,49 +149,50 @@
 }
 
 .el-table .fixed-width .el-button--small {
-	// padding-left: 0;
-	// padding-right: 0;
-	width: inherit;
+  // padding-left: 0;
+  // padding-right: 0;
+  width: inherit;
 }
 
 /** 表格更多操作下拉样式 */
 .el-table .el-dropdown-link {
-	cursor: pointer;
-	color: #409EFF;
-	margin-left: 10px;
+  cursor: pointer;
+  color: #409eff;
+  margin-left: 10px;
 }
 
-.el-table .el-dropdown, .el-icon-arrow-down {
-	font-size: 12px;
+.el-table .el-dropdown,
+.el-icon-arrow-down {
+  font-size: 12px;
 }
 
 .el-tree-node__content > .el-checkbox {
-	margin-right: 8px;
+  margin-right: 8px;
 }
 
 .list-group-striped > .list-group-item {
-	border-left: 0;
-	border-right: 0;
-	border-radius: 0;
-	padding-left: 0;
-	padding-right: 0;
+  border-left: 0;
+  border-right: 0;
+  border-radius: 0;
+  padding-left: 0;
+  padding-right: 0;
 }
 
 .list-group {
-	padding-left: 0px;
-	list-style: none;
+  padding-left: 0px;
+  list-style: none;
 }
 
 .list-group-item {
-	border-bottom: 1px solid #e7eaec;
-	border-top: 1px solid #e7eaec;
-	margin-bottom: -1px;
-	padding: 11px 0px;
-	font-size: 13px;
+  border-bottom: 1px solid #e7eaec;
+  border-top: 1px solid #e7eaec;
+  margin-bottom: -1px;
+  padding: 11px 0px;
+  font-size: 13px;
 }
 
 .pull-right {
-	float: right !important;
+  float: right !important;
 }
 
 // .el-card__header {
@@ -188,106 +201,106 @@
 // }
 
 .el-card__body {
-	padding: 15px 20px 20px 20px !important;
+  padding: 15px 20px 20px 20px !important;
 }
 
 .card-box {
-	padding-right: 15px;
-	padding-left: 15px;
-	margin-bottom: 10px;
+  padding-right: 15px;
+  padding-left: 15px;
+  margin-bottom: 10px;
 }
 
 /* button color */
 .el-button--cyan.is-active,
 .el-button--cyan:active {
-  background: #20B2AA;
-  border-color: #20B2AA;
-  color: #FFFFFF;
+  background: #20b2aa;
+  border-color: #20b2aa;
+  color: #ffffff;
 }
 
 .el-button--cyan:focus,
 .el-button--cyan:hover {
-  background: #48D1CC;
-  border-color: #48D1CC;
-  color: #FFFFFF;
+  background: #48d1cc;
+  border-color: #48d1cc;
+  color: #ffffff;
 }
 
 .el-button--cyan {
-  background-color: #20B2AA;
-  border-color: #20B2AA;
-  color: #FFFFFF;
+  background-color: #20b2aa;
+  border-color: #20b2aa;
+  color: #ffffff;
 }
 
 /* text color */
 .text-navy {
-	color: #1ab394;
+  color: #1ab394;
 }
 
 .text-primary {
-	color: inherit;
+  color: inherit;
 }
 
 .text-success {
-	color: #1c84c6;
+  color: #1c84c6;
 }
 
 .text-info {
-	color: #23c6c8;
+  color: #23c6c8;
 }
 
 .text-warning {
-	color: #f8ac59;
+  color: #f8ac59;
 }
 
 .text-danger {
-	color: #ed5565;
+  color: #ed5565;
 }
 
 .text-muted {
-	color: #888888;
+  color: #888888;
 }
 
 /* image */
 .img-circle {
-	border-radius: 50%;
+  border-radius: 50%;
 }
 
 .img-lg {
-	width: 120px;
-	height: 120px;
+  width: 120px;
+  height: 120px;
 }
 
 .avatar-upload-preview {
-	position: absolute;
-	top: 50%;
-	transform: translate(50%, -50%);
-	width: 200px;
-	height: 200px;
-	border-radius: 50%;
-	box-shadow: 0 0 4px #ccc;
-	overflow: hidden;
+  position: absolute;
+  top: 50%;
+  transform: translate(50%, -50%);
+  width: 200px;
+  height: 200px;
+  border-radius: 50%;
+  box-shadow: 0 0 4px #ccc;
+  overflow: hidden;
 }
 
 /* 拖拽列样式 */
-.sortable-ghost{
-	opacity: .8;
-	color: #fff!important;
-	background: #42b983!important;
+.sortable-ghost {
+  opacity: 0.8;
+  color: #fff !important;
+  background: #42b983 !important;
 }
 
 /* 表格右侧工具栏样式 */
 .top-right-btn {
-	margin-left: auto;
+  margin-left: auto;
 }
 
 .dialog-footer {
-	display: flex;
-	justify-content: flex-end;
+  display: flex;
+  justify-content: flex-end;
 }
 
 .search-form_container {
-	padding-top: 20px;
-	margin-bottom: 16px;
+  padding-top: 20px;
+  margin-bottom: 16px;
   background: #fff;
 }
 
@@ -299,11 +312,16 @@
 }
 
 .card-table_container {
-	padding-bottom: 0px !important;
+  padding-bottom: 0px !important;
+}
+
+.card-chart_container {
+  margin-bottom: 10px;
+  border: 0;
 }
 
 .chat-dialog_container {
   .el-dialog__body {
     background: #f3f2f7;
   }
-}
+}

+ 1 - 1
src/store/modules/user.js

@@ -35,7 +35,7 @@ const useUserStore = defineStore(
         return new Promise((resolve, reject) => {
           getInfo().then(res => {
             const user = res.user
-            const avatar = (user.avatar == "" || user.avatar == null) ? defAva : import.meta.env.VITE_APP_BASE_API + user.avatar;
+            const avatar = (user.avatar == "" || user.avatar == null) ? defAva : user.avatar;
 
             if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
               this.roles = res.roles

+ 172 - 0
src/views/business/comparison/components/LineEchart.vue

@@ -0,0 +1,172 @@
+<script setup>
+import { ref, onMounted, onUnmounted, computed } from 'vue';
+import * as echarts from 'echarts';
+import { getEchartData } from "@/api/business/comparison";
+import { getEchartLineOption } from "./echartConfig";
+
+let echart = null;
+let lastClickTime = new Date();
+
+const props = defineProps({
+  category: {
+    type: Array,
+    default: () => []
+  }
+})
+
+const echartRefDom = ref(null);
+
+const activeIndex = ref(0);
+
+const activeItem = computed(() => props.category[activeIndex.value]);
+
+const formatData = async () => {
+
+  echart.showLoading();
+
+  const data = await getEchartData({ category: activeItem.value.value });
+  
+  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 })
+
+  console.log("option", option);
+
+  echart.setOption(option);
+
+  echart.hideLoading();
+}
+
+const changeActive = (i) => {
+  activeIndex.value = i;
+  formatData();
+}
+
+const 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;
+  });
+}
+
+const windowResize = () => echart.resize();
+
+onMounted(async () => {
+
+  echart = echarts.init(echartRefDom.value);
+
+  window.addEventListener('resize', windowResize);
+
+  formatData();
+
+  legendselectchanged();
+})
+
+onUnmounted(() => {
+  window.addEventListener('resize', windowResize);
+})
+
+</script>
+
+<template>
+  <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>
+</template>
+
+<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>

+ 53 - 0
src/views/business/comparison/components/echartConfig.js

@@ -0,0 +1,53 @@
+export const getEchartLineOption = ({ xAxisData, echartData }) => {
+  const series = echartData.map(({ name, val: data }) => ({
+    name,
+    data,
+    type: "line",
+    showSymbol: false,
+    smooth: true,
+  }));
+
+  const option = {
+    grid: {
+      left: "5%",
+      top: "2%",
+      right: "12%",
+      bottom: "2%",
+      containLabel: true,
+    },
+    legend: {
+      left: "left",
+      icon: "circle",
+      orient: "vertical",
+      left: "88%",
+      top: "center",
+      itemWidth: 8,
+      itemHeight: 8,
+      padding: [0, 30],
+      selected: {
+        Real_1:true,
+        Real_2:false,
+        Real_3:false,
+        TFTpre_1: true,
+        TFTpre_2: false,
+        TFTpre_3: false,
+        LSTMpre_1: true,
+        LSTMpre_2: false,
+        LSTMpre_3: false
+      }
+    },
+    tooltip: {
+      trigger: "axis",
+    },
+    xAxis: {
+      type: "category",
+      boundaryGap: false,
+      data: xAxisData,
+    },
+    yAxis: {
+      type: "value",
+    },
+    series,
+  };
+  return option;
+};

+ 24 - 5
src/views/business/comparison/index.vue

@@ -17,8 +17,8 @@
           <el-option value="" label="全部"></el-option>
           <el-option
             :key="item"
-            :value="item"
-            :label="item"
+            :value="item.value"
+            :label="item.label"
             v-for="item in category"
           ></el-option>
         </el-select>
@@ -28,6 +28,10 @@
         <el-button icon="refresh" size="small" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+
+    <el-card shadow="never" class="card-chart_container">
+      <LineEchart :category="category"></LineEchart>
+      </el-card>
     <el-card shadow="never" body-class="card-table_container">
       <el-row :gutter="10" class="mb8">
         <el-col :span="1.5">
@@ -160,9 +164,11 @@
 <script>
 import { listComparison, getComparison, delComparison, addComparison, updateComparison } from "@/api/business/comparison";
 import dayjs from "dayjs";
+import LineEchart from "./components/LineEchart";
 export default {
+  components: { LineEchart },
   name: "Comparison",
-  data() {
+    data() {
     return {
       // 遮罩层
       loading: true,
@@ -183,7 +189,14 @@ export default {
       // 是否显示弹出层
       open: false,
       // 预测类型
-      category: ['cod', 'ss', 'zlsy', 'nh3', 'xsy1', 'xsy2'],
+      category: [
+        { label: "#1NO₃⁻", value: 'xsy1' },
+        { label: "#2NO₃⁻", value: 'xsy2' },
+        { label: "PO₄³⁻ ", value: 'zlsy' },
+        { label: "NH₃-N ", value: 'nh3' },
+        { label: "COD ", value: 'cod' },
+        { label: "SS ", value: 'ss' }
+      ],
       // 查询参数
       queryParams: {
         pageNum: 1,
@@ -213,7 +226,7 @@ export default {
         timeBegin = dayjs(begin).format('YYYY/MM/DD H');
         timeEnd = dayjs(end).format('YYYY/MM/DD H');
       }
-      console.log( this.addDateRange(this.queryParams) );
+      
       const params = { ...this.queryParams, timeBegin, timeEnd };
       delete params.daterange;
       listComparison(params).then(response => {
@@ -331,3 +344,9 @@ export default {
   }
 };
 </script>
+
+<style scoped lang="scss">
+.echart-wrap {
+  // height: 420px;
+}
+</style>