فهرست منبع

历史数据功能模块完成/全局scss代码优化

fanghuisheng 1 ماه پیش
والد
کامیت
4352464552

+ 81 - 1
src/assets/css/element.scss

@@ -43,15 +43,27 @@
 .el-tabs {
     --el-tabs-header-height: 50px;
 
+    &__header {
+        .el-tabs__item.is-active:hover {
+            color: #409eff !important;
+        }
+    }
+
     &__content {
         padding: 15px !important;
     }
 
     &__new-tab {
-        margin: 10px 10
+        margin: 10px 10;
+    }
+
+    &__header:hover,
+    &__item:hover {
+        color: #409eff !important;
     }
 }
 
+
 // 上传组件样式
 .el-upload-list {
     margin: 0px !important
@@ -63,4 +75,72 @@
         background: #FAFAFA !important;
         color: black
     }
+}
+
+// input输入框样式
+.el-input {
+    height: var(--el-input-height);
+
+
+    &__icon {
+        color: #409eff;
+    }
+
+    &__inner {
+        height: 36px;
+        line-height: 36px;
+
+        &:hover {
+            border-color: #409eff;
+        }
+
+        &:focus {
+            border-color: #409eff;
+        }
+    }
+}
+
+// form表单样式
+.el-form-item__content {
+    width: 250px;
+}
+
+// select下拉框样式
+.el-select {
+    width: 100%
+}
+
+// radio单选框样式
+.el-radio {
+    margin-right: 24px;
+
+    &__input.is-checked .el-radio__inner {
+        border-color: #409eff;
+        background: #409eff;
+    }
+
+    &__input.is-checked+.el-radio__label {
+        color: #409eff;
+    }
+}
+
+// tree树组件样式
+.el-tree-node__content {
+    position: relative;
+    font-size: 16px;
+}
+
+//公共复选样式
+.el-checkbox__input.is-checked .el-checkbox__inner {
+    background-color: #409eff;
+    border-color: #409eff;
+}
+
+.el-checkbox__input.is-checked+.el-checkbox__label {
+    color: #409eff;
+}
+
+.el-pagination .btn-prev .el-icon,
+.el-pagination .btn-next .el-icon {
+    padding-left: 10px;
 }

+ 137 - 84
src/assets/css/global.scss

@@ -16,113 +16,166 @@ a {
     outline: none;
     cursor: pointer;
     transition: color 0.3s;
-}
 
-a:active,
-a:hover {
-    text-decoration: none;
-    outline: 0;
-}
+    &:active {
+        color: #096dd9;
+    }
 
-a:active {
-    color: #096dd9;
-}
+    &:hover {
+        color: #40a9ff;
+    }
 
-a:hover {
-    color: #40a9ff;
+    &:active,
+    &:hover {
+        text-decoration: none;
+        outline: 0;
+    }
 }
 
 .border-solid-transparent {
     border: solid 1px transparent;
 }
 
-.text-align-right {
-    text-align: right;
+.text {
+    &-right {
+        text-align: right;
+    }
+
+    &-center {
+        text-align: center
+    }
 }
 
 .float-right {
     float: right;
 }
 
-.padding-t10 {
-    padding-top: 10px;
-}
-
 .cursor-pointer {
     cursor: pointer;
 }
 
-//公共tabs样式
-// .el-tabs__header .el-tabs__item.is-active {
-//     border-bottom: 2px solid #409eff;
-//     color: #409eff;
-// }
-.el-tabs__header .el-tabs__item.is-active:hover {
-    color: #409eff !important;
-}
-
-.el-tabs__header:hover,
-.el-tabs__item:hover {
-    color: #409eff !important;
-}
-
-//公共input样式
-.el-input {
-    height: var(--el-input-height);
-}
-.el-input__icon {
-    color: #409eff;
-}
-.el-input__inner {
-    height: 36px;
-    line-height: 36px
-}
-.el-input__inner:hover {
-    border-color: #409eff;
-}
-
-.el-input__inner:focus {
-    border-color: #409eff;
-}
-
-//公共单选样式
-.el-radio__input.is-checked .el-radio__inner {
-    border-color: #409eff;
-    background: #409eff;
-}
-
-.el-radio__input.is-checked+.el-radio__label {
-    color: #409eff;
-}
-
-//公共复选样式
-.el-checkbox__input.is-checked .el-checkbox__inner {
-    background-color: #409eff;
-    border-color: #409eff;
-}
-
-.el-checkbox__input.is-checked+.el-checkbox__label {
-    color: #409eff;
-}
-
-.el-pagination .btn-prev .el-icon,
-.el-pagination .btn-next .el-icon {
-    padding-left: 10px;
-}
-
-.el-table--border th:first-child .cell,
-.el-table--border td:first-child .cell {
-    // text-align: center;
-}
-
-.el-table .cell {
-    // text-align: center;
-}
-
 //公共字体无法选中
 .fontSizeSelect {
     -webkit-user-select: none;
     -moz-user-select: none;
     -ms-user-select: none;
     user-select: none;
+}
+
+
+
+
+.card-area {
+    font-size: 14px;
+
+    &-header {
+        display: flex;
+        width: 100%;
+
+        >.image {
+            width: 16px;
+            height: 16px;
+            margin-right: 10px;
+        }
+
+        >.title {
+            font-size: 14px;
+            font-weight: 600;
+            margin: 0 auto auto 0;
+        }
+
+        >.subTitle {
+            font-size: 12px;
+            margin: auto 0 0 0;
+        }
+    }
+
+    &-center {
+        display: flex;
+        width: 100%;
+
+        &.inline {
+            display: inline;
+        }
+
+        >.value {
+            display: flex;
+            white-space: nowrap;
+
+            >.image {
+                margin: auto 0;
+                width: 12px;
+                height: 8px;
+            }
+        }
+
+        .count {
+            display: flex;
+            flex-wrap: wrap;
+            width: 238px;
+            height: 132px;
+            padding: 10px;
+            background: #F2F3F8;
+            border: 1px solid #E9E9F3;
+
+            .title {
+                width: 60%;
+                font-size: 14px;
+                font-weight: 600;
+            }
+
+            .subTitle {
+                width: 40%;
+                font-size: 12px;
+                text-align: right;
+            }
+
+            .value {
+                width: 100%;
+                text-align: center;
+                font-size: 18px;
+                font-weight: 600;
+            }
+        }
+
+        .chart {
+            width: calc(100% - 258px);
+            margin: auto auto auto 20px;
+        }
+
+        >.countValue {
+            color: #41BED8;
+            font-size: 54px;
+            text-align: center;
+            margin-bottom: 10px;
+        }
+
+        >.unit {
+            font-size: 16px;
+            text-align: center;
+            color: #666666
+        }
+    }
+
+    &-footer {
+        display: flex;
+        color: #666666;
+
+        >.title {}
+
+        >.percent {
+            margin: auto 5px auto 10px;
+        }
+
+        >.image {
+            margin: auto 0;
+            width: 12px;
+            height: 8px;
+        }
+    }
+
+    &-divider {
+        height: 0.5px !important;
+        margin: 10px 0 !important;
+    }
 }

+ 7 - 69
src/assets/css/index.scss

@@ -62,10 +62,6 @@ a:hover {
     text-decoration: none;
 }
 
-div:focus {
-    outline: none;
-}
-
 .clearfix {
     &:after {
         visibility: hidden;
@@ -84,29 +80,21 @@ div:focus {
     text-overflow: ellipsis;
 }
 
-.text-center {
-    text-align: center
-}
-
-.delete-text {
-    color: #F80000 !important
-}
-
 .remarksTxt {
     opacity: .45;
     line-height: 1.5;
 }
 
-.bg-purple-dark {
-    border: 1px solid #99a9bf;
-}
-
 .bg-purple {
     border: 1px solid #d3dce6;
-}
 
-.bg-purple-light {
-    border: 1px solid #e5e9f2;
+    &-dark {
+        border: 1px solid #99a9bf;
+    }
+
+    &-light {
+        border: 1px solid #e5e9f2;
+    }
 }
 
 .grid-content {
@@ -188,20 +176,6 @@ div:focus {
     line-height: $vab-header-height !important;
 }
 
-// 台区列表
-.siteTitle {
-    font-size: 16px;
-    padding: 10px 20px 30px 20px;
-    text-align: center;
-    position: relative;
-
-    .goBack {
-        position: absolute;
-        left: 0;
-        top: 0
-    }
-}
-
 .status.el-avatar {
     width: 14px;
     height: 14px;
@@ -244,11 +218,6 @@ div:focus {
     background: #4074e7;
 }
 
-// input长度
-.el-form-item__content {
-    width: 250px;
-}
-
 .planOutage {
 
     .el-date-editor.el-input,
@@ -258,15 +227,6 @@ div:focus {
     }
 }
 
-.el-select {
-    width: 100%
-}
-
-// 单选框样式
-.el-radio {
-    margin-right: 24px
-}
-
 //提交:
 .sublitArea {
     text-align: right;
@@ -304,27 +264,11 @@ div:focus {
     text-align: right
 }
 
-// 树形控件icon
-.el-tree-node__content {
-    position: relative;
-    font-size: 16px;
-}
-
 .custom-tree-node span:first-child {
     width: 100%;
 }
 
 // 告警管理渐变背景色
-.alarmingTable {
-    a {
-        margin-right: 0 !important;
-    }
-}
-
-.alarmingManage .el-table .cell {
-    text-align: left !important;
-}
-
 .gradualBg {
     padding: 0 5px;
     color: #444;
@@ -418,12 +362,6 @@ div:focus {
 }
 
 // 谐波分析
-.harmonicReport {
-    .filter-container .filter-item {
-        // margin-right: 10px
-    }
-}
-
 .timeTab.el-button {
     margin-bottom: 20px;
     border-radius: 0

+ 7 - 1
src/assets/css/variables.module.scss

@@ -63,6 +63,13 @@
     }
 }
 
+@for $i from 1 through 5 {
+    .col-#{$i} {
+        width: calc(100% / #{ $i}) !important;
+    }
+}
+
+
 /* 文本
 ------------------------------- */
 @for $i from 10 through 500 {
@@ -72,7 +79,6 @@
 }
 
 $vab-color-blue: #1890ff;
-
 $vab-margin: 20px;
 $vab-padding: 20px;
 $vab-header-height: 54px;

+ 79 - 66
src/router/index.js

@@ -57,7 +57,7 @@ export const asyncRoutes = [
                 import('@/views/alarmManage/index'),
         },]
     },
-    
+
     {
         path: '/siteManage',
         redirect: '/siteManage/index',
@@ -205,23 +205,71 @@ export const asyncRoutes = [
     },
 
 
+    // {
+    //     meta: { icon: 'dataManage', title: '能源管理', },
+    //     path: '/energyManage',
+    //     component: Layout,
+    //     redirect: '/energyManage/totalEnergyC/index',
+    //     children: [
+    //         {
+    //             meta: { icon: 'totalEnergyC', title: '总能耗', },
+    //             path: 'totalEnergyC',
+    //             component: () =>
+    //                 import('@/views/energyManage/totalEnergyC/index'),
+    //         },
+    //         {
+    //             meta: { icon: 'energyStatistics', title: '能源统计', },
+    //             path: 'energyStatistics',
+    //             component: () =>
+    //                 import('@/views/energyManage/energyStatistics/index'),
+    //         },
+    //         {
+    //             meta: { icon: 'energyReport', title: '能源报表', },
+    //             path: 'energyReport',
+    //             component: () =>
+    //                 import('@/views/energyManage/energyReport/index'),
+    //         },
+    //     ]
+    // },
+
+
+    // {
+    //     meta: { icon: 'realMonitored', title: '实时监测', },
+    //     path: '/realMonitored',
+    //     component: Layout,
+    //     redirect: '/realMonitored/loadAnalysis/index',
+    //     children: [
+    //         {
+    //             meta: { icon: 'loadAnalysis', title: '负载分析', },
+    //             path: 'loadAnalysis',
+    //             component: () =>
+    //                 import('@/views/realMonitored/loadAnalysis/index'),
+    //         },
+    //         {
+    //             meta: { icon: 'loadAnalysis', title: '同比分析报表', },
+    //             path: 'loadAnalysis1',
+    //             component: () =>
+    //                 import('@/views/realMonitored/loadAnalysis/index'),
+    //         },
+    //     ]
+    // },
     {
-        meta: { icon: 'energyManage', title: '能源管理', },
-        path: '/energyManage',
+        meta: { icon: 'dataManage', title: '历史数据', },
+        path: '/historyData',
         component: Layout,
-        redirect: '/energyManage/totalEnergyC/index',
+        redirect: '/historyData/curve/index',
         children: [
             {
-                meta: { icon: 'totalEnergyC', title: '总能耗', },
-                path: 'totalEnergyC',
+                meta: { icon: 'curve', title: '历史曲线', },
+                path: 'curve',
                 component: () =>
-                    import('@/views/energyManage/totalEnergyC/index'),
+                    import('@/views/historyData/curve/index'),
             },
             {
-                meta: { icon: 'totalEnergyC', title: '同比分析报表', },
-                path: 'totalEnergyC1',
+                meta: { icon: 'report', title: '历史报表', },
+                path: 'report',
                 component: () =>
-                    import('@/views/energyManage/totalEnergyC/index'),
+                    import('@/views/historyData/report/index'),
             },
         ]
     },
@@ -271,27 +319,27 @@ export const asyncRoutes = [
     /**
      * monthReport 月度报告
      */
-    // {
-    //     path: '/monthReport',
-    //     redirect: '/monthReport',
-    //     meta: { title: '月度报告', icon: 'monthReport', },
-    //     component: Layout,
-    //     children: [{
-    //             meta: { title: '月度报告', icon: 'monthReport', },
-    //             path: '/monthReport',
-    //             component: () =>
-    //                 import ('@/views/monthReport/index'),
-    //             hidden: true
-    //         },
-    //         {
-    //             meta: { title: '月报模板', icon: 'reportModel', },
-    //             path: '/reportModel',
-    //             component: () =>
-    //                 import ('@/views/monthReport/reportModel'),
-    //             hidden: true
-    //         }
-    //     ]
-    // },
+    {
+        path: '/monthReport',
+        redirect: '/monthReport',
+        meta: { title: '月度报告', icon: 'monthReport', },
+        component: Layout,
+        children: [{
+            meta: { title: '月度报告', icon: 'monthReport', },
+            path: '/monthReport',
+            component: () =>
+                import('@/views/monthReport/index'),
+            hidden: true
+        },
+        {
+            meta: { title: '月报模板', icon: 'reportModel', },
+            path: '/reportModel',
+            component: () =>
+                import('@/views/monthReport/reportModel'),
+            hidden: true
+        }
+        ]
+    },
 
 
     {
@@ -428,8 +476,6 @@ export const asyncRoutes = [
     },
 
 
-
-
     // {
     //   path: '/test',
     //   component: Layout,
@@ -450,39 +496,6 @@ export const asyncRoutes = [
     //     },
     //   ],
     // },
-
-    // {
-    //     path: '/error',
-    //     name: 'Error',
-    //     component: Layout,
-    //     redirect: '/error/403',
-    //     meta: {
-    //         title: '错误页',
-    //         icon: 'error-warning-line',
-    //     },
-    //     children: [{
-    //             path: '403',
-    //             name: 'Error403',
-    //             component: () =>
-    //                 import ('@/views/403'),
-    //             meta: {
-    //                 title: '403',
-    //                 icon: 'error-warning-line',
-    //             },
-    //         },
-    //         {
-    //             path: '404',
-    //             name: 'Error404',
-    //             component: () =>
-    //                 import ('@/views/404'),
-    //             meta: {
-    //                 title: '404',
-    //                 icon: 'error-warning-line',
-    //             },
-    //         },
-    //     ],
-    // },
-
 ]
 
 const router = createRouter({

+ 107 - 0
src/views/historyData/curve/components/countChart.vue

@@ -0,0 +1,107 @@
+<template>
+  <div shadow="never" class="homeBoxCard">
+    <div class="height400" :id="props.id" :style="{ height: props.height + 'px' }" />
+  </div>
+</template>
+<script setup>
+/*----------------------------------依赖引入-----------------------------------*/
+import dayjs from 'dayjs';
+import * as echarts from 'echarts'
+import { ElMessage, ElNotification } from 'element-plus'
+import { ref, onMounted, watch, getCurrentInstance, reactive, toRefs } from 'vue'
+/*----------------------------------接口引入-----------------------------------*/
+/*----------------------------------组件引入-----------------------------------*/
+/*----------------------------------store引入-----------------------------------*/
+/*----------------------------------公共方法引入-----------------------------------*/
+/*----------------------------------公共变量-----------------------------------*/
+const props = defineProps({
+  id: String,
+  height: Number
+}) //数据双向绑定
+/*----------------------------------变量声明-----------------------------------*/
+
+function initEcharts(id, chartData) {
+  let myChart = echarts.init(document.getElementById(id))
+  // 绘制图表
+  myChart.setOption({
+    color: ['#41bfd8', '#FCD011'],
+    title: {
+      subtext: chartData.subtext
+    },
+    tooltip: {
+      trigger: 'axis',
+      // 关闭默认悬浮触发,改为手动控制
+      triggerOn: 'none'
+    },
+    legend: {
+      bottom: '0',
+      data: chartData.legendData
+    },
+    grid: {
+      left: '20',
+      right: '40',
+      top: '40',
+      bottom: '30',
+      containLabel: true,
+    },
+    calculable: true,
+    xAxis: [
+      {
+        type: 'category',
+        data: chartData.xAxisData,
+        boundaryGap: false,
+        axisTick: {
+          show: false, // 刻度线
+        },
+        axisLabel: {
+          interval: 17, // 间隔
+          formatter: function (value) {
+            return dayjs(value).format('HH:mm');
+          }
+        },
+        splitLine: { show: false }
+      }
+    ],
+    yAxis: chartData.yAxisData,
+    series: chartData.seriesData
+  })
+  window.addEventListener('resize', () => {
+    myChart.resize()
+  })
+
+  return myChart;
+}
+
+// 暴露变量
+defineExpose({
+  initEcharts,
+});
+</script>
+<style lang="scss" scoped>
+.homeBoxCard {
+
+  ::v-deep(.el-card__header) {
+    padding-left: 12px;
+    padding-right: 12px;
+  }
+
+  ::v-deep(.el-card__body) {
+    padding: 12px;
+    font-size: 14px;
+    line-height: 1.5715;
+  }
+
+  ::v-deep(.el-divider) {
+    margin: 8px 0;
+  }
+
+  .num {
+    font-size: 30px;
+    color: #515a6e;
+  }
+
+  .height400 {
+    height: 255px;
+  }
+}
+</style>

+ 533 - 0
src/views/historyData/curve/index.vue

@@ -0,0 +1,533 @@
+<template>
+    <el-radio-group v-model="dateRadio.value" style="margin:0 50px 20px 0">
+        <el-radio-button label="day" value="day">日</el-radio-button>
+        <el-radio-button label="month" value="month">月</el-radio-button>
+        <el-radio-button label="year" value="year">年</el-radio-button>
+    </el-radio-group>
+
+
+    <el-row :gutter="20">
+        <el-col :md="24" :lg="12" style="margin-bottom: 20px;" v-for="(area, index) in areaData" :key="index">
+            <el-card class="card-area">
+                <div class="card-area-header">
+                    <div class="title"> {{ area.title }} </div>
+                </div>
+                <div class="card-area-chart">
+                    <countChart :ref="'chart' + (index + 1)" :id="'chart' + (index + 1)" :height="300" />
+                </div>
+            </el-card>
+        </el-col>
+    </el-row>
+</template>
+<script setup>
+/*----------------------------------依赖引入-----------------------------------*/
+import { ElMessage, ElNotification } from 'element-plus'
+import { ref, onMounted, watch, getCurrentInstance, reactive, toRefs, nextTick } from 'vue'
+/*----------------------------------接口引入-----------------------------------*/
+/*----------------------------------组件引入-----------------------------------*/
+import countChart from './components/countChart.vue'
+/*----------------------------------store引入-----------------------------------*/
+/*----------------------------------公共方法引入-----------------------------------*/
+import dayjs from 'dayjs';
+import * as echarts from 'echarts'
+import { throttle } from 'lodash-es'; // 引入防抖/节流库
+/*----------------------------------公共变量-----------------------------------*/
+const props = defineProps({
+    dataType: String
+}) //数据双向绑定
+const emit = defineEmits([]);
+const { proxy } = getCurrentInstance();
+/*----------------------------------变量声明-----------------------------------*/
+const state = reactive({
+    dateRadio: {
+        value: "day"
+    },
+    areaData: [
+        {
+            title: "GYJLG_183信号",
+            chartLoading: false,
+            chartData: {
+                subtext: '',
+                legendData: ['设备信号强度'],
+                xAxisData: [],
+                seriesData: [
+                    { name: '设备信号强度', type: 'line', data: [], yAxisIndex: 0, },
+                ],
+                unit: "",
+                yAxisData: []
+            },
+        },
+        {
+            title: "低压进线",
+            chartLoading: false,
+            chartData: {
+                subtext: '',
+                legendData: ['A相电压(V)'],
+                xAxisData: [],
+                seriesData: [
+                    { name: 'A相电压(V)', type: 'line', data: [], yAxisIndex: 0, },
+                ],
+                unit: "V",
+                yAxisData: []
+            },
+        },
+        {
+            title: "进线电压",
+            chartLoading: false,
+            chartData: {
+                subtext: '',
+                legendData: ['A相电压(V)'],
+                xAxisData: [],
+                seriesData: [
+                    { name: 'A相电压(V)', type: 'line', data: [], yAxisIndex: 0, },
+                ],
+                unit: "V",
+                yAxisData: []
+            },
+        },
+        {
+            title: "需量",
+            chartLoading: false,
+            chartData: {
+                subtext: '',
+                legendData: ['实时需量(kW)'],
+                xAxisData: [],
+                seriesData: [
+                    { name: '实时需量(kW)', type: 'line', data: [], yAxisIndex: 0, },
+                ],
+                unit: "kW",
+                yAxisData: []
+            },
+        },
+        {
+            title: "总有功功率",
+            chartLoading: false,
+            chartData: {
+                subtext: '',
+                legendData: ['总有功功率(kW)'],
+                xAxisData: [],
+                seriesData: [
+                    { name: '总有功功率(kW)', type: 'line', data: [], yAxisIndex: 0, },
+                ],
+                unit: "kW",
+                yAxisData: []
+            },
+        },
+        {
+            title: "正有功电度",
+            chartLoading: false,
+            chartData: {
+                subtext: '',
+                legendData: ['正有功电度(kWh)'],
+                xAxisData: [],
+                seriesData: [
+                    { name: '正有功电度(kWh)', type: 'line', data: [], yAxisIndex: 0, },
+                ],
+                unit: "kWh",
+                yAxisData: []
+            },
+        },
+        {
+            title: "总进线三相电流",
+            chartLoading: false,
+            chartData: {
+                subtext: '',
+                legendData: ['A相电流(A)', 'B相电流(A)', 'C相电流(A)'],
+                xAxisData: [],
+                seriesData: [
+                    { name: 'A相电流(A)', type: 'line', data: [], yAxisIndex: 0, },
+                    { name: 'B相电流(A)', type: 'line', data: [], yAxisIndex: 0, },
+                    { name: 'C相电流(A)', type: 'line', data: [], yAxisIndex: 0, },
+                ],
+                unit: "A",
+                yAxisData: []
+            },
+        },
+        {
+            title: "总进线三相电压",
+            chartLoading: false,
+            chartData: {
+                subtext: '',
+                legendData: ['A相电压(kV)', 'B相电压(kV)', 'C相电压(kV)'],
+                xAxisData: [],
+                seriesData: [
+                    { name: 'A相电压(kV)', type: 'line', data: [], yAxisIndex: 0, },
+                    { name: 'B相电压(kV)', type: 'line', data: [], yAxisIndex: 0, },
+                    { name: 'C相电压(kV)', type: 'line', data: [], yAxisIndex: 0, },
+                ],
+                unit: "kV",
+                yAxisData: []
+            },
+
+        }
+    ],
+    dateTime: dayjs().format('YYYY-MM-DD'),
+})
+const { dateRadio, areaData } = toRefs(state)
+
+
+
+function generateTimeSlots(startDate, intervalMinutes) {
+    var today = dayjs(startDate).startOf('day'); // 获取当天的开始时间
+    var endOfDay = dayjs(startDate).endOf('day'); // 获取当天的结束时间
+    const slots = [];
+
+    while (today.isBefore(endOfDay)) {
+        slots.push(today.format('YYYY-MM-DD HH:mm')); // 添加当前时间到数组
+        today = today.add(intervalMinutes, 'minute'); // 增加间隔时间
+    }
+
+    return slots;
+}
+
+
+function init_FPG_Data(startDate) {
+    return {
+        yAxisIndex: 1,
+        type: 'line',
+        markArea: {
+            data: [
+                [
+                    {
+                        name: `谷\n0.3406元`,
+                        yAxis: 0,
+                        xAxis: dayjs(startDate).format('YYYY-MM-DD 00:00'),
+                        itemStyle: {
+                            color: 'rgba(184, 232 , 197, 0.7)', // 区域填充色
+                        },
+                        label: {
+                            color: '#b8e8c5'
+                        }
+                    },
+                    {
+                        yAxis: 0.3406,
+                        xAxis: dayjs(startDate).format('YYYY-MM-DD 06:00'),
+                    }
+                ],
+                [
+                    {
+                        name: '平\n0.7279元',
+                        yAxis: 0,
+                        xAxis: dayjs(startDate).format('YYYY-MM-DD 06:00'),
+                        itemStyle: {
+                            color: 'rgba(178, 210 , 242, 0.7)', // 区域填充色
+                        },
+                        label: {
+                            color: '#b2d2f2'
+                        }
+                    },
+                    {
+                        yAxis: 0.7279,
+                        xAxis: dayjs(startDate).format('YYYY-MM-DD 08:00'),
+                    }
+                ],
+                [
+                    {
+                        name: '峰\n1.2442元',
+                        yAxis: 0,
+                        xAxis: dayjs(startDate).format('YYYY-MM-DD 08:00'),
+                        itemStyle: {
+                            color: 'rgba(251, 191, 191, 0.7)', // 区域填充色
+                        },
+                        label: {
+                            color: '#fbbfbf'
+                        }
+                    },
+                    {
+                        yAxis: 1.2442,
+                        xAxis: dayjs(startDate).format('YYYY-MM-DD 15:00'),
+                    }
+                ],
+                [
+                    {
+                        name: '平\n0.7279元',
+                        yAxis: 0,
+                        xAxis: dayjs(startDate).format('YYYY-MM-DD 15:00'),
+                        itemStyle: {
+                            color: 'rgba(178, 210 , 242, 0.7)', // 区域填充色
+                        },
+                        label: {
+                            color: '#b2d2f2'
+                        }
+                    },
+                    {
+                        yAxis: 0.7279,
+                        xAxis: dayjs(startDate).format('YYYY-MM-DD 18:00'),
+                    }
+                ],
+                [
+                    {
+                        name: '峰\n1.2442元',
+                        yAxis: 0,
+                        xAxis: dayjs(startDate).format('YYYY-MM-DD 18:00'),
+                        itemStyle: {
+                            color: 'rgba(251, 191, 191, 0.7)', // 区域填充色
+                        },
+                        label: {
+                            color: '#fbbfbf'
+                        }
+                    },
+                    {
+                        yAxis: 1.2442,
+                        xAxis: dayjs(startDate).format('YYYY-MM-DD 21:00'),
+                    }
+                ],
+                [
+                    {
+                        name: '平\n0.7279元',
+                        yAxis: 0,
+                        xAxis: dayjs(startDate).format('YYYY-MM-DD 21:00'),
+                        itemStyle: {
+                            color: 'rgba(178, 210 , 242, 0.7)', // 区域填充色
+                        },
+                        label: {
+                            color: '#b2d2f2'
+                        }
+                    },
+                    {
+                        yAxis: 0.7279,
+                        xAxis: dayjs(startDate).format('YYYY-MM-DD 22:00'),
+                    }
+                ],
+                [
+                    {
+                        name: `谷\n0.3406元`,
+                        yAxis: 0,
+                        xAxis: dayjs(startDate).format('YYYY-MM-DD 22:00'),
+                        itemStyle: {
+                            color: 'rgba(184, 232 , 197, 0.7)', // 区域填充色
+                        },
+                        label: {
+                            color: '#b8e8c5'
+                        }
+                    },
+                    {
+                        yAxis: 0.3406,
+                        xAxis: dayjs(startDate).format('YYYY-MM-DD 23:55'),
+                    }
+                ],
+            ]
+        }
+    }
+}
+
+function init_yAxis_Data(num) {
+    const yAxis1 = {
+        type: 'value',
+        axisTick: {
+            show: true, // 刻度线
+        },
+        splitLine: {
+            show: true,
+            lineStyle: { type: 'dashed', color: '#eee' }
+        },
+        axisLine: {
+            show: true, // 轴线
+        },
+    }
+
+    const yAxis2 = {
+        type: 'value',
+        axisTick: {
+            show: true, // 刻度线
+        },
+        min: 0,
+        max: 1.5,
+        interval: 0.3,
+        position: 'right', // 显示在右侧
+        splitLine: { show: false },// 不与左侧 Y 轴共享刻度(可选)
+        axisLine: {
+            show: true, // 轴线
+        },
+    }
+
+    if (num == 1) {
+        return [yAxis1,]
+    } else if (num == 2) {
+        return [yAxis1, yAxis2,]
+    }
+}
+
+
+function initChartData() {
+    const timeSlots = generateTimeSlots(state.dateTime, 5);
+    const chartData = []
+
+    state.areaData.forEach((event, index) => {
+        event.chartData.xAxisData = timeSlots;
+        event.chartData.seriesData.forEach((item) => {
+            item.data = Array.from({ length: timeSlots.length }, () => index + 800)
+        })
+        event.chartData.yAxisData = init_yAxis_Data(1)
+
+        if (event.chartData.unit == 'kW' || event.chartData.unit == 'kWh') {
+            event.chartData.seriesData[event.chartData.seriesData.length] = init_FPG_Data(state.dateTime)
+            event.chartData.yAxisData = init_yAxis_Data(2)
+        }
+
+        chartData.push(proxy.$refs['chart' + (index + 1)][0].initEcharts('chart' + (index + 1), event.chartData))
+    })
+
+    syncTooltip(chartData)
+}
+
+function syncTooltip(charts) {
+    // 1. 状态管理:记录当前激活的 tooltip
+    let activeTooltipIndex = -1;
+    let activePoint = null;
+
+    // 2. 节流处理鼠标事件(每 50ms 执行一次)
+    const handleMouseMove = throttle((event, chartIndex) => {
+        const pointInPixel = [event.offsetX, event.offsetY];
+        const targetX = pointInPixel[0];
+        const targetY = pointInPixel[1];
+
+        // 3. 计算鼠标位置对应的 xAxis 索引(只处理网格区域内的事件)
+        if (!charts[chartIndex].containPixel('grid', pointInPixel)) {
+            if (activeTooltipIndex !== -1) {
+                hideAllTooltips();
+                activeTooltipIndex = -1;
+                activePoint = null;
+            }
+            return;
+        }
+
+        // 4. 获取当前鼠标位置对应的 xAxis 索引
+        const xAxisIndex = Math.round(charts[chartIndex].convertFromPixel('grid', [targetX, targetY])[0]);
+        const isNewPoint = activePoint === null ||
+            activePoint[0] !== xAxisIndex ||
+            activePoint[1] !== chartIndex;
+
+        if (isNewPoint) {
+            // 5. 只有当鼠标移动到新的数据点时才更新 tooltip
+            activePoint = [xAxisIndex, chartIndex];
+            updateAllTooltips(xAxisIndex);
+        }
+    }, 50); // 节流时间 50ms,可根据需要调整
+
+    // 6. 批量更新所有图表的 tooltip
+    function updateAllTooltips(xAxisIndex) {
+        charts.forEach(chart => {
+            chart.dispatchAction({
+                type: 'showTip',
+                seriesIndex: 0, // 假设只有一个 series,根据实际情况调整
+                dataIndex: xAxisIndex
+            });
+
+            chart.setOption({
+                tooltip: {
+                    axisPointer: { type: 'line' },
+                }
+            });
+        });
+        activeTooltipIndex = xAxisIndex;
+    }
+
+    // 7. 隐藏所有图表的 tooltip
+    function hideAllTooltips() {
+        charts.forEach(chart => {
+            chart.dispatchAction({
+                type: 'hideTip',
+            });
+
+            chart.setOption({
+                tooltip: {
+                    axisPointer: { type: 'none' },
+                }
+            });
+        });
+    }
+
+    // 8. 绑定事件监听器
+    charts.forEach((chart, index) => {
+        // 鼠标移动事件(使用节流处理)
+        chart.getZr().on('mousemove', (event) => handleMouseMove(event, index));
+
+        // 鼠标离开图表区域时隐藏所有 tooltip
+        chart.getZr().on('mouseout', () => {
+            if (activeTooltipIndex !== -1) {
+                hideAllTooltips();
+                activeTooltipIndex = -1;
+                activePoint = null;
+            }
+        });
+    });
+}
+
+
+onMounted(() => {
+    setTimeout(() => {
+        initChartData()
+    }, 500);
+})
+
+// 暴露变量
+defineExpose({
+});
+</script>
+<style scoped>
+.chart-container {
+    display: flex;
+    flex-direction: column;
+    gap: 20px;
+}
+
+.chart {
+    width: 100%;
+    height: 400px;
+}
+</style>
+<style lang="scss" scoped>
+.card-area {
+    font-size: 14px;
+
+    &-header {
+        display: flex;
+        width: 100%;
+
+        >.title {
+            font-size: 14px;
+            font-weight: 600;
+        }
+
+        >.subTitle {
+            font-size: 12px;
+            margin: auto 0 0 0;
+        }
+    }
+
+    &-center {
+        width: 100%;
+
+        >.value {
+            color: #41BED8;
+            font-size: 54px;
+            text-align: center;
+            margin-bottom: 10px;
+        }
+
+        >.unit {
+            font-size: 16px;
+            text-align: center;
+            color: #666666
+        }
+    }
+
+    &-footer {
+        display: flex;
+        color: #666666;
+
+        >.title {}
+
+        >.percent {
+            margin: auto 5px auto 10px;
+        }
+
+        >.image {
+            margin: auto 0;
+            width: 12px;
+            height: 8px;
+        }
+    }
+}
+</style>

+ 107 - 0
src/views/historyData/report/index.vue

@@ -0,0 +1,107 @@
+<template>
+    <el-tabs v-model="editableTabsValue" type="border-card" editable class="demo-tabs" @edit="handleTabsEdit">
+        <el-tab-pane v-for="item in editableTabs" :key="item.name" :label="item.title" :name="item.name">
+            <div style="margin-bottom:15px">
+                <el-date-picker v-model="datePicker.value" :type="datePicker.type" :format="datePicker.format"
+                    placeholder="请选择日期" :clearable="false" />
+            </div>
+
+            <el-table :data="tableData" stripe style="width: 100%">
+                <el-table-column prop="date" label="时间" width="180" />
+                <el-table-column prop="name" label="A相电流(A)" width="180" />
+                <el-table-column prop="address" label="B相电流(A)" />
+            </el-table>
+        </el-tab-pane>
+    </el-tabs>
+</template>
+<script setup>
+/*----------------------------------依赖引入-----------------------------------*/
+import { ElMessage, ElNotification } from 'element-plus'
+import { ref, onMounted, watch, getCurrentInstance, reactive, toRefs } from 'vue'
+/*----------------------------------接口引入-----------------------------------*/
+/*----------------------------------组件引入-----------------------------------*/
+/*----------------------------------store引入-----------------------------------*/
+/*----------------------------------公共方法引入-----------------------------------*/
+import dayjs from 'dayjs';
+/*----------------------------------公共变量-----------------------------------*/
+const props = defineProps({
+    dataType: String
+}) //数据双向绑定
+const emit = defineEmits([]);
+const { proxy } = getCurrentInstance();
+/*----------------------------------变量声明-----------------------------------*/
+const state = reactive({
+    editableTabsValue: '1',
+    editableTabs: [{
+        title: '点位数据',
+        name: '1',
+        content: 'Tab 1 content',
+    }],
+    tableData: [
+        {
+            date: '01:00',
+            name: '37.17',
+            address: '36.855',
+        },
+        {
+            date: '02:00',
+            name: '51.525',
+            address: '50.88',
+        },
+        {
+            date: '03:00',
+            name: '50.88',
+            address: '50.88',
+        },
+        {
+            date: '04:00',
+            name: '50.88',
+            address: '50.88',
+        },
+    ],
+    datePicker: {
+        type: "date",
+        format: "YYYY-MM-DD",
+        value: dayjs().format('YYYY-MM-DD'),
+    }
+})
+const { editableTabsValue, editableTabs, tableData, datePicker } = toRefs(state)
+
+
+let tabIndex = 2
+function handleTabsEdit(targetName, action) {
+    if (action === 'add') {
+        const newTabName = `${++tabIndex}`
+        editableTabs.value.push({
+            title: 'New Tab',
+            name: newTabName,
+            content: 'New Tab content',
+        })
+        editableTabsValue.value = newTabName
+    } else if (action === 'remove') {
+        const tabs = editableTabs.value
+        let activeName = editableTabsValue.value
+        if (activeName === targetName) {
+            tabs.forEach((tab, index) => {
+                if (tab.name === targetName) {
+                    const nextTab = tabs[index + 1] || tabs[index - 1]
+                    if (nextTab) {
+                        activeName = nextTab.name
+                    }
+                }
+            })
+        }
+
+        editableTabsValue.value = activeName
+        editableTabs.value = tabs.filter((tab) => tab.name !== targetName)
+    }
+}
+
+onMounted(() => {
+})
+
+// 暴露变量
+defineExpose({
+});
+</script>
+<style lang="scss" scoped></style>