a10f3b82e33756ed0cd62a0cbe83bab8674df16f..d0f98ad8e1047e3161a458399ad3005404ed87b8
2025-06-06 whychdw
标准参数管理
d0f98a 对比 | 目录
2025-06-06 he wei
UA 整理提交
33d2b1 对比 | 目录
28个文件已修改
33个文件已添加
7107 ■■■■ 已修改文件
src/api/alarm.js 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/realtime.js 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/station.js 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/statistic.js 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/bg-login0.jpg 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/bg-login1.png 补丁 | 查看 | 原始文档 | blame | 历史
src/components/HdwButton/images/primary-bg.png 补丁 | 查看 | 原始文档 | blame | 历史
src/components/HdwButton/images/warning-bg.png 补丁 | 查看 | 原始文档 | blame | 历史
src/components/HdwButton/index.vue 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/TabButton/images/active-bg.png 补丁 | 查看 | 原始文档 | blame | 历史
src/components/TabButton/images/bg.png 补丁 | 查看 | 原始文档 | blame | 历史
src/components/TabButton/index.vue 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/echarts/BaseChart.vue 242 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/echarts/bar1.vue 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/echarts/line1.vue 213 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/echarts/line2.vue 259 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/echarts/line3.vue 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/echarts/transparent.js 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/noData.vue 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/pwButton.vue 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/data-line.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/dc-power.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/download.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/dun.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/export.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/search-data.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/standard-params.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/components/Navbar.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/modules/alarm.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/modules/datas.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/modules/dcPowerStatus.js 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/modules/statistics.js 43 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/styles/blue.css 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/styles/blue.less 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/styles/index.css 83 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/styles/index.less 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/echartsEvent.js 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/formatSeconds.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/passiveEventListener.js 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/alarm/battAlarm.vue 247 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/alarm/battHisAlarm.vue 602 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/alarm/devAlarm.vue 237 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/alarm/pwrAlarm.vue 236 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dashboard/index.vue 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/datas/addEdit.vue 90 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/datas/device.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dcPowerStatus/standardParams.vue 218 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/history/hisReal.vue 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/history/hisTest.vue 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/history/index.vue 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login/index.vue 145 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/realtime/index.vue 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/realtime/tabs/system.vue 238 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/statistics/batt.vue 438 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/statistics/battCell.vue 554 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/statistics/battCompare0.vue 546 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/statistics/battCompare1.vue 594 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/statistics/device.vue 422 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/statistics/power.vue 499 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/alarm.js
@@ -40,4 +40,15 @@
      num
    }
  });
}
/**
 * 电池历史告警
 */
export function getBattAlmHis(data) {
  return request({
    url: "almHis/getBattAlmHis",
    method: "POST",
    data
  });
}
src/api/realtime.js
New file
@@ -0,0 +1,39 @@
import request from '@/utils/request';
/**
 * 系统概览获取半小时核容设备信息
 * battgroupId
 * granularity
 */
export function getHalfHourBattDevData(battgroupId, granularity) {
  return request({
    url: 'real/getHalfHourBattDevData',
    method: 'GET',
    params: { battgroupId, granularity }
  });
}
/**
 * 系统概览获取半小时交流输入统计
 * powerId
 * granularity
 */
export function getHalfHourPwrHisAcinData(powerId, granularity) {
  return request({
    url: 'real/getHalfHourPwrHisAcinData',
    method: 'GET',
    params: { powerId, granularity }
  });
}
/** 系统概览获取半小时直流输出统计
 * powerId
 * granularity
 */
export function getHalfHourPwrHisDcoutData(powerId, granularity) {
  return request({
    url: 'real/getHalfHourPwrHisDcoutData',
    method: 'GET',
    params: { powerId, granularity }
  });
}
src/api/station.js
@@ -123,7 +123,7 @@
 */
export function getDevList(data) {
  return request({
    url: 'condition/getInfo',
    url: 'info/getInfo',
    method: 'POST',
    data
  });
@@ -200,6 +200,16 @@
}
/**
 * 获取标称容量(下拉)
 */
export function getMonCapByUid() {
  return request({
    url: 'condition/getMonCapByUid',
    method: 'GET'
  });
}
/**
 * 获取标称单体电压(下拉)
 */
export function getMonVol() {
@@ -214,7 +224,7 @@
 */
export function addDev(data) {
  return request({
    url: 'condition/addInfo',
    url: 'info/addInfo',
    method: 'POST',
    data
  });
@@ -225,7 +235,7 @@
 */
export function updateDev(data) {
  return request({
    url: 'condition/updateInfo',
    url: 'info/updateInfo',
    method: 'POST',
    data
  });
@@ -237,7 +247,7 @@
 */
export function delBatt(stationId, powerId, battgroupId) {
  return request({
    url: 'condition/delBatt',
    url: 'info/delBatt',
    method: 'GET',
    params: { stationId, powerId, battgroupId }
  });
@@ -248,7 +258,7 @@
 */
export function addBatt(data) {
  return request({
    url: 'condition/addBatt',
    url: 'info/addBatt',
    method: 'POST',
    data
  });
@@ -262,4 +272,66 @@
    url: 'stationInf/getLeftStation',
    method: 'GET'
  });
}
// ===================2025-06-04=====================
/**
 * 获取电池告警类型(下拉)
 */
export function getBattAlarmIdType() {
  return request({
    url: 'condition/getAlarmIdType',
    method: 'GET',
  });
}
/**
 * 获取电池组工作状态类型(下拉)
 */
export function getBattState() {
  return request({
    url: 'condition/getBattState',
    method: 'GET',
  });
}
/**
 * 获取设备告警类型(下拉)
 */
export function getDevAlmIdType() {
  return request({
    url: 'condition/getDevAlmIdType',
    method: 'GET',
  });
}
/**
 * 获取设备工作类型(下拉)
 */
export function getDevState() {
  return request({
    url: 'condition/getDevState',
    method: 'GET',
  });
}
/**
 * 获取电源告警类型(下拉)
 */
export function getPowerAlmIdType() {
  return request({
    url: 'condition/getPwrAlmIdType',
    method: 'GET',
  });
}
/**
 * 电池性能下拉
 */
export function getBattPerformance() {
  return request({
    url: 'condition/getCapperformance',
    method: 'GET',
  });
}
src/api/statistic.js
@@ -31,4 +31,83 @@
    method: "POST",
    data
  });
}
/**
 * 蓄电池组信息统计
 */
export function getBattStatistic(data) {
  return request({
    url: "statistic/getBattStatistic",
    method: "POST",
    data
  });
}
/**
 * 设备信息统计
 */
export function getDeviceStatistic(data) {
  return request({
    url: "statistic/getDevStatistic",
    method: "POST",
    data
  });
}
/**
 * 单节数量统计
 */
export function getSingleStatistic(data) {
  return request({
    url: "statistic/getMonStatistic",
    method: "POST",
    data
  });
}
/**
 * 电源信息统计
 */
export function getPowerStatistic(data) {
  return request({
    url: "statistic/getPowerStatistic",
    method: "POST",
    data
  });
}
/**
 * 蓄电池组对比分析界面(同一品牌同一时间)(1.2.15)
 */
export function getBattCompare15(data) {
  return request({
    url: "statistic/getBattCompare15Statistic",
    method: "POST",
    data
  });
}
/**
 * 蓄电池组对比分析界面(不同品牌同一时间)(1.2.16)
 */
export function getBattCompare16(data) {
  return request({
    url: "statistic/getBattCompare16Statistic",
    method: "POST",
    data
  });
}
/**
 * 单节数最统计导出
 */
export function battMonExport(data) {
  return request({
    url: "export/exportBattTinfStatistic",
    method: "POST",
    fullRes: true,
    responseType: "blob",
    data
  });
}
src/assets/images/bg-login0.jpg
src/assets/images/bg-login1.png
src/components/HdwButton/images/primary-bg.png
src/components/HdwButton/images/warning-bg.png
src/components/HdwButton/index.vue
New file
@@ -0,0 +1,48 @@
<script setup lang="ts">
import {defaultProps} from "element-plus";
const props = defineProps({
  type: {
    type: String,
    default: "primary"
  },
  iconClass: {
    type: String,
    default: "export"
  }
});
</script>
<template>
  <div class="hdw-button-wrapper" :class="{'warning': type==='warning'}">
    <svg-icon class="btn-icon" :icon-class="iconClass"></svg-icon>
    <slot></slot>
  </div>
</template>
<style scoped lang="less">
.hdw-button-wrapper {
  user-select: none;
  display: inline-block;
  padding: 8px 12px;
  text-align: center;
  font-size: 16px;
  background-image: url("./images/primary-bg.png");
  background-size: 100% 100%;
  background-repeat: no-repeat;
  cursor: pointer;
  &:hover {
    font-weight: 700;
  }
  &:active {
    color: #0BF9FE;
  }
  &.warning {
    background-image: url("./images/warning-bg.png");
  }
  .btn-icon {
    font-size: 20px;
    margin-right: 8px;
  }
}
</style>
src/components/TabButton/images/active-bg.png
src/components/TabButton/images/bg.png
src/components/TabButton/index.vue
New file
@@ -0,0 +1,37 @@
<script setup>
const props = defineProps({
  active: {
    type: Boolean,
    default: false
  }
});
</script>
<template>
  <div class="tab-button-wrapper" :class="{'active': active}">
    <slot></slot>
  </div>
</template>
<style scoped lang="less">
.tab-button-wrapper {
  display: inline-block;
  user-select: none;
  padding: 12px 48px;
  text-align: center;
  background-image: url("./images/bg.png");
  background-size: 100% 100%;
  background-repeat: no-repeat;
  cursor: pointer;
  font-size: 16px;
  &:hover {
    font-weight: 700;
  }
  &:active {
    background-image: url("./images/active-bg.png");
  }
  &.active {
    background-image: url("./images/active-bg.png");
  }
}
</style>
src/components/echarts/BaseChart.vue
@@ -3,8 +3,8 @@
import "./transparent";
import { onMounted, onBeforeUnmount, ref, nextTick } from "vue";
const chart_instance = ref(null);
let exportChart_instance = ref(null);
let chart_instance = null;
let exportChart_instance = null;
const chart = ref(null);
const exportChart = ref(null);
@@ -15,14 +15,12 @@
  onMounted(() => {
    console.log('base mounted',chart.value, '=============');
    
    chart_instance.value = echarts.init(chart.value, "transparent");
    exportChart_instance.value = echarts.init(exportChart.value, "transparent");
    let option = getOptions();
    // chart_instance.value = echarts.init(chart.value, "transparent");
    chart_instance = echarts.init(chart.value, "transparent");
    exportChart_instance = echarts.init(exportChart.value, "transparent");
    nextTick(() => {
      chart_instance.value.resize();
      chart_instance.value.setOption(option);
      chart_instance.resize();
    });
    window.addEventListener("resize", resize);
@@ -33,213 +31,13 @@
    dispose();
  });
  function getOptions() {
        const option = {
            // title: {
            //     text: '请求数',
            //     textStyle: {
            //         fontWeight: 'normal',
            //         fontSize: 16,
            //         color: '#F1F1F3'
            //     },
            //     left: '6%'
            // },
            tooltip: {
                trigger: 'axis',
                axisPointer: {
                    lineStyle: {
                        color: '#57617B'
                    }
                }
            },
            // legend: {
            //     icon: 'rect',
            //     itemWidth: 14,
            //     itemHeight: 5,
            //     itemGap: 13,
            //     data: ['移动', '电信', '联通'],
            //     right: '4%',
            //     textStyle: {
            //         fontSize: 12,
            //         color: '#F1F1F3'
            //     }
            // },
            grid: {
                left: '3%',
                right: '4%',
                bottom: '3%',
                containLabel: true
            },
            xAxis: [{
                type: 'category',
                boundaryGap: false,
                axisLine: {
                    lineStyle: {
                        color: '#57617B'
                    }
                },
                data: ['13:00', '13:05', '13:10', '13:15', '13:20', '13:25', '13:30', '13:35', '13:40', '13:45', '13:50', '13:55']
            }, {
                axisPointer: {
                    show: false
                },
                axisLine: {
                    lineStyle: {
                        color: '#57617B'
                    }
                },
                axisTick: {
                    show: false
                },
                position: 'bottom',
                offset: 20,
                data: ['', '', '', '', '', '', '', '', '', '', {
                    value: '周六',
                    textStyle: {
                        align: 'left'
                    }
                }, '周日']
            }],
            yAxis: [{
                type: 'value',
                name: '单位(%)',
                axisTick: {
                    show: false
                },
                axisLine: {
                    lineStyle: {
                        color: '#57617B'
                    }
                },
                axisLabel: {
                    margin: 10,
                    textStyle: {
                        fontSize: 14
                    }
                },
                splitLine: {
                    lineStyle: {
                        color: '#57617B'
                    }
                }
            }],
            series: [{
                name: '移动',
                type: 'line',
                smooth: true,
                symbol: 'circle',
                symbolSize: 5,
                showSymbol: false,
                lineStyle: {
                    normal: {
                        width: 1
                    }
                },
                areaStyle: {
                    normal: {
                        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                            offset: 0,
                            color: 'rgba(137, 189, 27, 0.3)'
                        }, {
                            offset: 0.8,
                            color: 'rgba(137, 189, 27, 0)'
                        }], false),
                        shadowColor: 'rgba(0, 0, 0, 0.1)',
                        shadowBlur: 10
                    }
                },
                itemStyle: {
                    normal: {
                        color: 'rgb(137,189,27)',
                        borderColor: 'rgba(137,189,2,0.27)',
                        borderWidth: 12
                    }
                },
                data: [220, 182, 191, 134, 150, 120, 110, 125, 145, 122, 165, 122]
            }, {
                name: '电信',
                type: 'line',
                smooth: true,
                symbol: 'circle',
                symbolSize: 5,
                showSymbol: false,
                lineStyle: {
                    normal: {
                        width: 1
                    }
                },
                areaStyle: {
                    normal: {
                        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                            offset: 0,
                            color: 'rgba(0, 136, 212, 0.3)'
                        }, {
                            offset: 0.8,
                            color: 'rgba(0, 136, 212, 0)'
                        }], false),
                        shadowColor: 'rgba(0, 0, 0, 0.1)',
                        shadowBlur: 10
                    }
                },
                itemStyle: {
                    normal: {
                        color: 'rgb(0,136,212)',
                        borderColor: 'rgba(0,136,212,0.2)',
                        borderWidth: 12
                    }
                },
                data: [120, 110, 125, 145, 122, 165, 122, 220, 182, 191, 134, 150]
            }, {
                name: '联通',
                type: 'line',
                smooth: true,
                symbol: 'circle',
                symbolSize: 5,
                showSymbol: false,
                lineStyle: {
                    normal: {
                        width: 1
                    }
                },
                areaStyle: {
                    normal: {
                        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                            offset: 0,
                            color: 'rgba(219, 50, 51, 0.3)'
                        }, {
                            offset: 0.8,
                            color: 'rgba(219, 50, 51, 0)'
                        }], false),
                        shadowColor: 'rgba(0, 0, 0, 0.1)',
                        shadowBlur: 10
                    }
                },
                itemStyle: {
                    normal: {
                        color: 'rgb(219,50,51)',
                        borderColor: 'rgba(219,50,51,0.2)',
                        borderWidth: 12
                    }
                },
                data: [220, 182, 125, 145, 122, 191, 134, 150, 120, 110, 165, 122]
            },]
        };
        return option;
    }
  function getChart() {
    return chart_instance.value;
    return chart_instance;
  }
  function setOption(option) {
    if (chart_instance.value) {
      chart_instance.value.setOption(option);
    if (chart_instance) {
      chart_instance.setOption(option);
    }
  }
@@ -252,8 +50,8 @@
  function getDataURL() {
    let base64 = "";
    if (exportChart_instance.value) {
      let option = chart_instance.value.getOption();
    if (exportChart_instance) {
      let option = chart_instance.getOption();
      option.xAxis[0].axisLine.lineStyle = {
        color: "#000",
      };
@@ -263,8 +61,8 @@
        color: "#000",
      };
      option.yAxis[0].axisLabel.textStyle.color = "#000";
      exportChart_instance.value.setOption(option);
      base64 = exportChart_instance.value.getDataURL({
      exportChart_instance.setOption(option);
      base64 = exportChart_instance.getDataURL({
        pixelRatio: 1,
        backgroundColor: "#fff",
      });
@@ -273,16 +71,14 @@
  }
  function resize() {
    if (chart_instance.value) {
      chart_instance.value.resize();
      console.log('resize', '=============');
    if (chart_instance) {
      chart_instance.resize();
    }
  }
  function dispose() {
    disposeChart(chart_instance.value);
    disposeChart(exportChart_instance.value);
    disposeChart(chart_instance);
    disposeChart(exportChart_instance);
  }
  function disposeChart(chart) {
@@ -306,9 +102,9 @@
  <div
    class="e-chart-root"
    :class="{ 'full-screen': fullScreenFlag }"
    @click="handleClick"
    @dblclick="fullScreen"
  >
    <!-- @click="handleClick"
    @dblclick="fullScreen" -->
    <div class="e-chart-container">
      <div class="e-chart" ref="chart"></div>
      <slot name="tools"></slot>
src/components/echarts/bar1.vue
New file
@@ -0,0 +1,214 @@
<script setup>
    import { onMounted, ref, watchEffect, nextTick, onBeforeUnmount } from "vue";
    import * as echarts from 'echarts';
  import baseChart from "./BaseChart.vue";
  const chart = ref(null);
  const props = defineProps({
    type: {
      type: String,
      default: '电流'
    },
    title: {
      type: String,
      default: ''
    },
    unit: {
      type: String,
      default: ''
    }
  });
  const barWidth = 60;
  function createLinearColor(color) {
    return new echarts.graphic.LinearGradient(0, 0, 1, 0, [{
        offset: 0,
        color,
      }, {
        offset: 0.5,
        color: 'transparent'
      }, {
        offset: 1,
        color
      }
    ], false)
  }
  const colors = ['#12E876', '#E87615', '#0AE3E5'];
  function getMax(data) {
    let max = Math.max.apply(null, data) * 1.2;
    return max || 1;
  }
 function getOptions(xLabels, datas) {
    xLabels = xLabels || [];
    datas = datas || [];
        const option = {
            // title: {
            //     text: props.title,
            //     textStyle: {
            //         fontWeight: 'normal',
            //         fontSize: 14,
            //         color: '#fff'
            //     },
            //     left: '6%'
            // },
            tooltip: {
                trigger: 'axis',
                axisPointer: {
                    lineStyle: {
                        color: '#fff'
                    }
                }
            },
            grid: {
                left: '3%',
                right: '4%',
                bottom: '3%',
        top: 30,
                containLabel: true
            },
            xAxis: [{
                type: 'category',
                // boundaryGap: false,
                axisLine: {
                    lineStyle: {
                        color: '#fff'
                    }
                },
        offset: 16,
                data: xLabels
            }],
            yAxis: [{
                type: 'value',
                name: props.unit,
                axisTick: {
                    show: true,
                },
                axisLine: {
          show: true,
                    lineStyle: {
                        color: '#fff'
                    }
                },
                axisLabel: {
                    margin: 10,
                    fontSize: 12,
          color: '#fff'
                },
                splitLine: {
                    lineStyle: {
                        color: 'rgba(255,255,255,0.2)'
                    }
                },
        max: getMax(datas),
            }],
            series: [
        {
                type: 'bar',
        barGap: "-100%",
        barWidth,
        tooltip: {
          show: false
        },
        itemStyle: {
          color: 'rgba(255,255,255,0.1)',
        },
                data: datas.map(v => getMax(datas)),
        z: 0
            },
       {
                type: 'bar',
                smooth: true,
        barWidth,
        label: {
          show: true,
          position: "top",
          offset: [0, -18],
          fontSize: 20,
          fontWeight: "bolder",
          color: "#ebf006",
        },
        itemStyle: {
          color: function (params) {
              return colors.map(v => createLinearColor(v))[params.dataIndex % 3];
          },
        },
                data: datas
            }, {  // 实体柱状图顶部
            z: 3,
            type: 'pictorialBar',
            symbolPosition: 'end',
            data: datas,
            barWidth,
            tooltip: {
              show: false,
            },
            symbolOffset: ['0%', '-50%'],
            // symbolRepeat: 'true',
            symbolSize: ['100%',  30],
            // symbol: 'path://M 100 50 A 40,40 0 1,0 100,50.1 z',
            // symbol: 'path://M0,10 L10,10 L10,0 L0,0 M14,10 L24,10 L24,0 L14,0',
            itemStyle: {
              borderWidth: 0,
              color: function (params) {
                  return colors[params.dataIndex % 3];
              },
            },
        },]
        };
        return option;
    }
  let myChart = null;
  let chart_instance = null;
    function initChart() {
        if (chart.value) {
            // myChart = chart.value.getChart();
            let option = getOptions();
      chart.value.setOption(option);
      chart_instance = chart.value.getChart();
        }
    }
    function updateChart(xLabels, datas) {
    if (chart.value) {
            let option = getOptions(xLabels, datas);
      chart.value.setOption(option);
        }
    }
  function getChart() {
    return chart_instance;
  }
  defineExpose({
    getChart,
    updateChart
  });
    onMounted(() => {
    // console.log('line mounted', '=============');
        initChart();
    });
</script>
<template>
  <div class="line-chart">
    <base-chart ref="chart"></base-chart>
  </div>
</template>
<style scoped>
.line-chart {
  width: 100%;
  height: 100%;
}
</style>
src/components/echarts/line1.vue
@@ -4,44 +4,48 @@
  import baseChart from "./BaseChart.vue";
  const chart = ref(null);
  const props = defineProps({
    type: {
      type: String,
      default: '电流'
    },
    title: {
      type: String,
      default: ''
    },
    unit: {
      type: String,
      default: ''
    }
  });
    function getOptions() {
 function getOptions(xLabels, datas) {
    xLabels = xLabels || [];
    datas = datas || [];
        const option = {
            title: {
                text: '请求数',
                textStyle: {
                    fontWeight: 'normal',
                    fontSize: 16,
                    color: '#F1F1F3'
                },
                left: '6%'
            },
            // title: {
            //     text: props.title,
            //     textStyle: {
            //         fontWeight: 'normal',
            //         fontSize: 14,
            //         color: '#fff'
            //     },
            //     left: '6%'
            // },
            tooltip: {
                trigger: 'axis',
                axisPointer: {
                    lineStyle: {
                        color: '#57617B'
                        color: '#fff'
                    }
                }
            },
            // legend: {
            //     icon: 'rect',
            //     itemWidth: 14,
            //     itemHeight: 5,
            //     itemGap: 13,
            //     data: ['移动', '电信', '联通'],
            //     right: '4%',
            //     textStyle: {
            //         fontSize: 12,
            //         color: '#F1F1F3'
            //     }
            // },
            grid: {
                left: '3%',
                right: '4%',
                bottom: '3%',
        top: 30,
                containLabel: true
            },
            xAxis: [{
@@ -49,158 +53,62 @@
                boundaryGap: false,
                axisLine: {
                    lineStyle: {
                        color: '#57617B'
                        color: '#fff'
                    }
                },
                data: ['13:00', '13:05', '13:10', '13:15', '13:20', '13:25', '13:30', '13:35', '13:40', '13:45', '13:50', '13:55']
            }, {
                axisPointer: {
                    show: false
                },
                axisLine: {
                    lineStyle: {
                        color: '#57617B'
                    }
                },
                axisTick: {
                    show: false
                },
                position: 'bottom',
                offset: 20,
                data: ['', '', '', '', '', '', '', '', '', '', {
                    value: '周六',
                    textStyle: {
                        align: 'left'
                    }
                }, '周日']
                data: xLabels
            }],
            yAxis: [{
                type: 'value',
                name: '单位(%)',
                name: props.unit,
                axisTick: {
                    show: false
                    show: true,
                },
                axisLine: {
          show: true,
                    lineStyle: {
                        color: '#57617B'
                        color: '#fff'
                    }
                },
                axisLabel: {
                    margin: 10,
                    textStyle: {
                        fontSize: 14
                    }
                    fontSize: 12,
          color: '#fff'
                },
                splitLine: {
                    lineStyle: {
                        color: '#57617B'
                        color: 'rgba(255,255,255,0.2)'
                    }
                }
            }],
            series: [{
                name: '移动',
                name: props.type,
                type: 'line',
                smooth: true,
                symbol: 'circle',
                symbolSize: 5,
                showSymbol: false,
                lineStyle: {
                    normal: {
                        width: 1
                    }
          width: 1
                },
                areaStyle: {
                    normal: {
                        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                            offset: 0,
                            color: 'rgba(137, 189, 27, 0.3)'
                        }, {
                            offset: 0.8,
                            color: 'rgba(137, 189, 27, 0)'
                        }], false),
                        shadowColor: 'rgba(0, 0, 0, 0.1)',
                        shadowBlur: 10
                    }
          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
            offset: 0,
            color: 'rgba(0, 136, 212, 0.3)'
          }, {
            offset: 0.8,
            color: 'rgba(0, 136, 212, 0)'
          }], false),
          shadowColor: 'rgba(0, 0, 0, 0.1)',
          shadowBlur: 10
                },
                itemStyle: {
                    normal: {
                        color: 'rgb(137,189,27)',
                        borderColor: 'rgba(137,189,2,0.27)',
                        borderWidth: 12
                    }
          color: 'rgb(0,136,212)',
          borderColor: 'rgba(0,136,212,0.2)',
          borderWidth: 12
                },
                data: [220, 182, 191, 134, 150, 120, 110, 125, 145, 122, 165, 122]
            }, {
                name: '电信',
                type: 'line',
                smooth: true,
                symbol: 'circle',
                symbolSize: 5,
                showSymbol: false,
                lineStyle: {
                    normal: {
                        width: 1
                    }
                },
                areaStyle: {
                    normal: {
                        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                            offset: 0,
                            color: 'rgba(0, 136, 212, 0.3)'
                        }, {
                            offset: 0.8,
                            color: 'rgba(0, 136, 212, 0)'
                        }], false),
                        shadowColor: 'rgba(0, 0, 0, 0.1)',
                        shadowBlur: 10
                    }
                },
                itemStyle: {
                    normal: {
                        color: 'rgb(0,136,212)',
                        borderColor: 'rgba(0,136,212,0.2)',
                        borderWidth: 12
                    }
                },
                data: [120, 110, 125, 145, 122, 165, 122, 220, 182, 191, 134, 150]
            }, {
                name: '联通',
                type: 'line',
                smooth: true,
                symbol: 'circle',
                symbolSize: 5,
                showSymbol: false,
                lineStyle: {
                    normal: {
                        width: 1
                    }
                },
                areaStyle: {
                    normal: {
                        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                            offset: 0,
                            color: 'rgba(219, 50, 51, 0.3)'
                        }, {
                            offset: 0.8,
                            color: 'rgba(219, 50, 51, 0)'
                        }], false),
                        shadowColor: 'rgba(0, 0, 0, 0.1)',
                        shadowBlur: 10
                    }
                },
                itemStyle: {
                    normal: {
                        color: 'rgb(219,50,51)',
                        borderColor: 'rgba(219,50,51,0.2)',
                        borderWidth: 12
                    }
                },
                data: [220, 182, 125, 145, 122, 191, 134, 150, 120, 110, 165, 122]
            },]
                data: datas
            }]
        };
        return option;
@@ -218,15 +126,18 @@
    }
    function updateChart(xLabels, datas) {
        let option = getOptions(xLabels, datas);
    // if (myChart) {
    //   myChart.setOption(option);
    // }
    if (chart.value) {
            let option = getOptions(xLabels, datas);
      chart.value.setOption(option);
        }
    }
  defineExpose({
    updateChart
  });
    onMounted(() => {
    console.log('line mounted', '=============');
    // console.log('line mounted', '=============');
    
        initChart();
    });
src/components/echarts/line2.vue
New file
@@ -0,0 +1,259 @@
<script setup>
    import { onMounted, ref, reactive, watchEffect, nextTick, onBeforeUnmount } from "vue";
    import * as echarts from 'echarts';
    import baseChart from "./BaseChart.vue";
    const chart = ref(null);
    const props = defineProps({
        title: {
            type: String,
            default: ''
        },
        unit: {
            type: String,
            default: ''
        }
    });
    const typeList = ['设备温度', '组端电流', '组端电压', '负载电流'];
    // 这两个数值是百分比
    const baseTop = 10;
    const gridHeight = 20;
    function makeXAxis(gridIndex, opt) {
        return echarts.util.merge({
            type: 'category',
      boundaryGap: false,
            gridIndex: gridIndex,
            axisLine: { onZero: false, lineStyle: { color: '#ddd' } },
            axisTick: { show: false },
            axisLabel: { show: false },
            splitLine: { show: false, lineStyle: { color: '#ddd' } },
            // axisPointer: {
            //     lineStyle: { color: 'transparent' }
            // }
        }, opt || {}, true);
    }
    function makeYAxis(gridIndex, opt) {
        return echarts.util.merge({
            type: 'value',
            gridIndex: gridIndex,
            nameLocation: 'middle',
            nameTextStyle: {
                color: '#fff',
                lineHeight: 18
            },
            nameRotate: 0,
            boundaryGap: ['30%', '30%'],
            axisTick: { show: false },
            axisLine: { lineStyle: { color: '#ccc' } },
            axisLabel: { show: false },
            splitLine: { show: false }
        }, opt || {}, true);
    }
    function makeGrid(top, opt) {
        return echarts.util.merge({
            top: top + '%',
            height: gridHeight + '%',
            left: '15%',
            right: 14,
        }, opt || {}, true);
    }
    function getSeries(datas) {
        let res = [];
        typeList.forEach((item, index) => {
            res.push({
                type: 'line',
                smooth: true,
                symbol: 'circle',
                symbolSize: 5,
                showSymbol: false,
                xAxisIndex: index,
                yAxisIndex: index,
                lineStyle: {
                    width: 1
                },
                data: datas[item] || [],
            });
        });
        return res;
    }
    function getOptions(xLabels, datas) {
        xLabels = xLabels || [];
        datas = datas || {};
        let series = getSeries(datas);
        const option = {
            // title: {
            //     text: props.title,
            //     textStyle: {
            //         fontWeight: 'normal',
            //         fontSize: 14,
            //         color: '#fff'
            //     },
            //     left: '6%'
            // },
      color: ['#1186ce', '#e5c619', '#1d1dfd', '#2dbfae'],
            tooltip: {
                trigger: 'axis',
                axisPointer: {
                    lineStyle: {
                        color: '#fff'
                    }
                },
                formatter: function (params) {
                    if (params.length) {
            // params.unshift({ seriesName: 'time', value: Math.floor(params[0].value), color: '#5193f2' })
                        let res = typeList.map((seriesName, idx) => {
                            for (var i = 0; i < params.length; i++) {
                                var param = params[i];
                                var style = 'color: ' + param.color;
                                if (param.seriesIndex === idx) {
                                    return '<span style="' + style + '">'
                                        + seriesName
                                        + ':</span><span style="'
                                        + style + '">' + param.value + '</span>';
                                }
                            }
                        }).join('<br>');
            console.log('res', res, '=============');
            return res;
                    }
                }
            },
            axisPointer: {
                link: [{ xAxisIndex: 'all' }],
        lineStyle: {
          color: '#fff'
        },
                snap: true
            },
            grid: [
                makeGrid(baseTop),
                makeGrid(baseTop + gridHeight),
                makeGrid(baseTop + gridHeight * 2),
                makeGrid(baseTop + gridHeight * 3),
                // makeGrid(baseTop + gridHeight * 3, {
                //     height: gridHeight - 10,
                //     borderColor: '#ccc',
                //     borderWidth: 1,
                //     z: 10
                // }),
            ],
            xAxis: [
                makeXAxis(0, {
                    axisLine: { show: false }
                }),
                makeXAxis(1, {
                    axisLine: { show: false }
                }),
                makeXAxis(2, {
                    axisLine: { show: false }
                }),
        makeXAxis(3, {
          data: xLabels,
          axisTick: { show: true },
          axisLabel: { show: true },
          // boundaryGap: false,
          axisLine: {
            lineStyle: {
              color: '#fff'
            }
          },
        }),
                // makeXAxis(4, {
                //     position: 'top',
                //     axisLine: { show: false, onZero: false },
                //     splitLine: { show: true },
                //     axisLabel: { show: true, textStyle: { color: '#555' } },
                //     axisPointer: {
                //         show: true,
                //         lineStyle: {
                //             color: '#478cf1',
                //             width: 1.5
                //         }
                //     }
                // })
            ],
            // xAxis: [{
            //     type: 'category',
            //     boundaryGap: false,
            //     axisLine: {
            //         lineStyle: {
            //             color: '#fff'
            //         }
            //     },
            //     data: xLabels
            // }],
            yAxis: [
                makeYAxis(0, {
                    name: typeList[0].replace(/(.{2})/g, '$1\n').replace(/\n$/, ''),
                }),
                makeYAxis(1, {
                    name: typeList[1].replace(/(.{2})/g, '$1\n').replace(/\n$/, ''),
                }),
                makeYAxis(2, {
                    name: typeList[2].replace(/(.{2})/g, '$1\n').replace(/\n$/, ''),
                }),
                makeYAxis(3, {
                    name: typeList[3].replace(/(.{2})/g, '$1\n').replace(/\n$/, ''),
                })
            ],
            series
        };
        return option;
    }
    let myChart = null;
    function initChart() {
        if (chart.value) {
            // myChart = chart.value.getChart();
            let option = getOptions();
            chart.value.setOption(option);
        }
    }
    function updateChart(xLabels, datas) {
        if (chart.value) {
            let option = getOptions(xLabels, datas);
            chart.value.setOption(option);
        }
    }
    defineExpose({
        updateChart
    });
    onMounted(() => {
        // console.log('line mounted', '=============');
        initChart();
    });
</script>
<template>
  <div class="line-chart">
    <base-chart ref="chart"></base-chart>
  </div>
</template>
<style scoped>
.line-chart {
  width: 100%;
  height: 100%;
}
</style>
src/components/echarts/line3.vue
New file
@@ -0,0 +1,144 @@
<script setup>
    import { onMounted, ref, watchEffect, nextTick, onBeforeUnmount } from "vue";
    import * as echarts from 'echarts';
  import baseChart from "./BaseChart.vue";
  const chart = ref(null);
  const props = defineProps({
    title: {
      type: String,
      default: ''
    },
    unit: {
      type: String,
      default: ''
    }
  });
 function getOptions(xLabels, datas) {
    xLabels = xLabels || [];
    datas = datas || [];
        const option = {
            // title: {
            //     text: props.title,
            //     textStyle: {
            //         fontWeight: 'normal',
            //         fontSize: 14,
            //         color: '#fff'
            //     },
            //     left: '6%'
            // },
            tooltip: {
                trigger: 'axis',
                axisPointer: {
                    lineStyle: {
                        color: '#fff'
                    }
                }
            },
            grid: {
                left: '3%',
                right: '4%',
                bottom: '3%',
        top: 30,
                containLabel: true
            },
            xAxis: [{
                type: 'category',
                // boundaryGap: false,
                axisLine: {
                    lineStyle: {
                        color: '#fff'
                    }
                },
                data: xLabels
            }],
            yAxis: [{
                type: 'value',
                name: props.unit,
                axisTick: {
                    show: true,
                },
                axisLine: {
          show: true,
                    lineStyle: {
                        color: '#fff'
                    }
                },
                axisLabel: {
                    margin: 10,
                    fontSize: 12,
          color: '#fff'
                },
                splitLine: {
                    lineStyle: {
                        color: 'rgba(255,255,255,0.2)'
                    }
                }
            }],
            series: [{
                // name: props.type,
                type: 'line',
                smooth: true,
                symbol: 'circle',
                symbolSize: 5,
                showSymbol: false,
                lineStyle: {
          width: 1
                },
                data: datas
            }]
        };
        return option;
    }
  let myChart = null;
  let chart_instance = null;
    function initChart() {
        if (chart.value) {
            // myChart = chart.value.getChart();
            let option = getOptions();
      chart.value.setOption(option);
      chart_instance = chart.value.getChart();
        }
    }
    function updateChart(xLabels, datas) {
    if (chart.value) {
            let option = getOptions(xLabels, datas);
      chart.value.setOption(option);
        }
    }
  function getChart() {
    return chart_instance;
  }
  defineExpose({
    getChart,
    updateChart
  });
    onMounted(() => {
    // console.log('line mounted', '=============');
        initChart();
    });
</script>
<template>
  <div class="line-chart">
    <base-chart ref="chart"></base-chart>
  </div>
</template>
<style scoped>
.line-chart {
  width: 100%;
  height: 100%;
}
</style>
src/components/echarts/transparent.js
@@ -46,50 +46,6 @@
    mapBorderWidthE: 1,
    mapAreaColor: "#eee",
    mapAreaColorE: "rgba(255,215,0,0.8)",
    axes: [
      {
        type: "all",
        name: "通用坐标轴",
        axisLineShow: true,
        axisLineColor: "#6E7079",
        axisTickShow: true,
        axisTickColor: "#6E7079",
        axisLabelShow: true,
        axisLabelColor: "#6E7079",
        splitLineShow: true,
        splitLineColor: ["#E0E6F1"],
        splitAreaShow: false,
        splitAreaColor: ["rgba(250,250,250,0.2)", "rgba(210,219,238,0.2)"],
      },
      {
        type: "category",
        name: "类目坐标轴",
        axisLineShow: true,
        axisLineColor: "#ffffff",
        axisTickShow: true,
        axisTickColor: "#00ffff",
        axisLabelShow: true,
        axisLabelColor: "#ffffff",
        splitLineShow: false,
        splitLineColor: ["#E0E6F1"],
        splitAreaShow: true,
        splitAreaColor: ["rgba(160,158,222,0.2)", "rgba(210,219,238,0.2)"],
      },
      {
        type: "value",
        name: "数值坐标轴",
        axisLineShow: true,
        axisLineColor: "#ffffff",
        axisTickShow: true,
        axisTickColor: "#00ffff",
        axisLabelShow: true,
        axisLabelColor: "#00ffff",
        splitLineShow: true,
        splitLineColor: ["#E0E6F1"],
        splitAreaShow: false,
        splitAreaColor: ["rgba(250,250,250,0.2)", "rgba(210,219,238,0.2)"],
      },
    ],
    axisSeperateSetting: true,
    toolboxColor: "#00ffff",
    toolboxEmphasisColor: "#ffffff",
@@ -109,4 +65,5 @@
  },
};
echarts.registerTheme("transparent", theme);
echarts.registerTheme("transparent", theme.theme);
console.log('Theme registered:', echarts, "echarts.getTheme('transparent')");
src/components/noData.vue
New file
@@ -0,0 +1,42 @@
<script setup>
const props = defineProps({
  text: {
    type: String,
    default: "暂无数据"
  }
});
</script>
<template>
<div class="no-data-wrapper">
  <div class="no-data-content">
    <div class="no-data-icon">
      <svg-icon class="icon" icon-class="search-data"></svg-icon>
    </div>
    <div class="no-data-text">{{ props.text }}</div>
  </div>
</div>
</template>
<style scoped lang="less">
.no-data-wrapper {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
  .no-data-content {
    display: inline-block;
    text-align: center;
    color: #00feff;
    .no-data-icon {
      margin-bottom: 8px;
      .icon {
        color: #00feff;
        font-size: 40px;
      }
    }
  }
}
</style>
src/components/pwButton.vue
New file
@@ -0,0 +1,41 @@
<script setup>
const props = defineProps({
  active: {
    type: Boolean,
    default: false
  },
  text: {
    type: String,
    default: ""
  }
});
</script>
<template>
  <div class="pw-button-container" :class="{'active':props.active}">
    <div class="pw-button-wrapper">
      <div class="pw-button-text">{{ text }}</div>
    </div>
  </div>
</template>
<style scoped lang="less">
.pw-button-container {
  user-select: none;
  display: inline-block;
  border: 1px solid #4095e3;
  font-size: 14px;
  padding: 4px 16px;
  box-sizing: border-box;
  min-width: 80px;
  text-align: center;
  cursor: pointer;
  &.active {
    background: linear-gradient(to top right, #5eb0fb, #529adc,#022345, #022345);
  }
  &:active {
    background: linear-gradient(to top right, #5eb0fb, #529adc,#022345, #022345);
  }
}
</style>
src/icons/svg/data-line.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1749111380616" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="998" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M932.460289 856.177778H157.86635c-6.593939 0-13.187879 0-19.911111-6.59394-6.593939-6.593939-6.593939-13.187879-6.59394-13.187878V102.141414c0-13.187879-6.593939-33.09899-19.911111-39.692929-13.187879-6.593939-33.09899-6.593939-46.286868 0C51.975441 69.042424 45.381501 82.359596 45.381501 102.141414v734.125253c0 33.09899 13.187879 59.60404 33.09899 79.385858C98.391602 935.692929 124.76736 942.286869 157.86635 942.286869h774.464646c26.505051 0 46.286869-19.911111 46.286869-46.286869 0.129293-19.911111-19.652525-39.822222-46.157576-39.822222z" p-id="999"></path><path d="M852.945138 456.40404l76.8-0.129293v55.983839l-74.60202 1.422222s-35.684848 2.715152-47.062627-21.333333c-9.309091-19.523232-43.830303-75.377778-43.70101-75.377778l-91.668687 377.147475c-2.19798 11.894949-12.412121 20.29899-24.436363 20.29899h-0.646465c-12.153535-0.258586-28.832323-9.438384-30.513131-21.59192l-91.539394-495.191919-76.282828 372.105051c-2.19798 12.024242-20.169697 20.040404-31.418182 20.428283-12.153535-0.258586-26.246465-9.179798-28.056566-21.333334l-33.228283-231.175757-17.713131 61.931313c-3.10303 10.731313-12.8 17.971717-23.789899 17.971717H166.011804v-53.527273h122.569697l52.363637-155.927272c3.232323-11.248485 13.575758-18.618182 25.6-17.971718 11.636364 0.775758 21.20404 9.69697 22.884848 21.204041l35.814142 198.852525 79.773737-398.610101c2.19798-12.024242 13.575758-20.686869 25.082828-20.29899 12.153535 0.258586 22.367677 9.438384 24.048485 21.462626l98.004041 534.755556 81.583838-337.066667c2.19798-12.024242 13.705051-20.428283 24.953535-20.428283 12.153535 0.258586 27.539394 9.179798 29.349495 21.333334m-112.355555 497.131313" p-id="1000"></path></svg>
src/icons/svg/dc-power.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1749111045533" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="967" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M853.333333 952.888889H170.666667a56.96 56.96 0 0 1-56.888889-56.888889V128a56.96 56.96 0 0 1 56.888889-56.888889h682.666666a56.96 56.96 0 0 1 56.888889 56.888889v768a56.96 56.96 0 0 1-56.888889 56.888889zM678.030222 582.272v89.443556h115.413334v-89.443556h-115.413334z m-153.856 0v89.443556h115.413334v-89.443556h-115.413334z m-153.856 0v89.443556h115.413334v-89.443556h-115.413334z m-153.912889 0v89.443556h115.384889v-89.443556h-115.384889z m0-230.058667v89.443556H793.457778v-89.443556H216.405333z" p-id="968"></path></svg>
src/icons/svg/download.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1749194662142" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="944" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M827.8 925.4H198.2c-72.6 0-131.6-56.3-131.6-125.4V676.1c0-24.7 20-44.6 44.6-44.6s44.6 20 44.6 44.6V800c0 19.9 19 36.1 42.4 36.1h629.6c23.3 0 42.3-16.2 42.3-36.1V676.1c0-24.7 20-44.6 44.6-44.6 24.7 0 44.6 20 44.6 44.6V800c0.1 69.1-58.9 125.4-131.5 125.4z" p-id="945"></path><path d="M514.6 778.1c-29.6 0-57.8-12.1-79.4-34L225 531c-24.6-25-31.6-62.6-17.7-95.7 12.9-30.8 40.7-49.9 72.5-49.9h41.3V223.2c0-68.9 51.5-125 114.8-125H590c63.3 0 114.8 56.1 114.8 125v163.4h41.7c31.8 0 59.6 19.1 72.5 49.8 13.9 33.1 7.1 70.6-17.4 95.7L594.3 743.8c-21.6 22-49.8 34.2-79.5 34.2-0.1 0.1-0.1 0.1-0.2 0.1z m-220-303.5l204.2 206.8c4.8 4.8 10.2 7.4 15.8 7.4 5.6 0 11.1-2.6 15.8-7.4L731.6 476h-71.4c-24.7 0-44.6-20-44.6-44.6V223.2c0-19-11.9-35.7-25.5-35.7H436c-13.6 0-25.5 16.7-25.5 35.7v206.7c0 24.7-20 44.6-44.6 44.6h-71.3z m475.2 26.2h0.2-0.2z" p-id="946"></path></svg>
src/icons/svg/dun.svg
New file
@@ -0,0 +1 @@
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M834.869 175.034c-7.668 0.278-15.222 0.385-22.612 0.385h-0.032c-194.413 0-280.058-89.102-280.771-89.87L512.027 64.28l-19.454 21.268c-0.848 0.932-91.22 96.715-303.444 89.486l-27.255-0.93v403.924c0 105.752 35.307 260.001 340.618 378.013l9.509 3.68 9.504-3.68C826.815 838.03 862.123 683.78 862.123 578.028V174.104l-27.254 0.93zM477.186 697.78L273.372 517.181l54.153-43.882 108.368 79.957s149.63-154.798 294.097-227.034l20.636 23.245s-180.57 149.635-273.44 348.312z"></path></svg>
src/icons/svg/export.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1749175390074" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5246" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M972.8 614.4c-30.72 0-51.2 20.48-51.2 51.2v61.44c0 107.52-5.12 194.56-204.8 194.56H307.2c-204.8 0-204.8-92.16-204.8-204.8v-256C87.04 302.08 163.84 266.24 256 256h5.12c30.72 0 51.2-20.48 51.2-51.2s-20.48-51.2-51.2-51.2H204.8C92.16 153.6 0 245.76 0 358.4v460.8C0 931.84 92.16 1024 204.8 1024h614.4c112.64 0 204.8-92.16 204.8-204.8V665.6c0-25.6-25.6-51.2-51.2-51.2zM179.2 640c0 51.2 10.24 102.4 25.6 148.48 56.32-168.96 215.04-291.84 399.36-291.84v66.56c0 25.6 15.36 51.2 40.96 66.56 10.24 5.12 20.48 10.24 30.72 10.24 15.36 0 30.72-5.12 40.96-15.36l281.6-220.16c15.36-15.36 25.6-35.84 25.6-61.44 0-20.48-10.24-46.08-25.6-56.32L716.8 66.56c-15.36-10.24-30.72-15.36-46.08-15.36-10.24 0-20.48 0-30.72 10.24-25.6 10.24-40.96 35.84-40.96 66.56v71.68c-230.4 0-419.84 194.56-419.84 440.32z m0 0" p-id="5247"></path></svg>
src/icons/svg/search-data.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1749194816996" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1131" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M614.942 923.829H192.031c-24.757 0-48.55-10.12-66.078-28.25-17.469-18.071-27.347-42.647-27.287-68.187V167.455c0-25.6 9.818-50.116 27.287-68.247 17.528-18.07 41.32-28.31 66.078-28.31h639.276c24.757 0 48.55 10.18 66.078 28.31 17.65 18.191 27.287 42.406 27.287 68.186l-1.446 436.465v0.12a34.997 34.997 0 0 0 34.335 35.238c18.913 0 34.334-15.661 34.575-35.117l1.385-436.525v-0.12c0-44.454-17.047-87.04-47.465-118.483A159.623 159.623 0 0 0 831.307 0H192.031C149.023 0 107.7 17.589 77.282 49.032a170.044 170.044 0 0 0-47.405 118.483v659.877c-0.06 44.394 16.987 87.04 47.405 118.423 30.42 31.442 71.68 49.091 114.749 49.031h422.851a34.936 34.936 0 0 0 34.455-35.539 34.936 34.936 0 0 0-34.395-35.478z" p-id="1132"></path><path d="M983.582 934.25L852.27 798.72c62.284-90.413 47.104-215.04-34.996-286.72a202.09 202.09 0 0 0-279.733 12.047c-76.017 78.487-81.077 204.017-11.686 288.768 69.452 84.751 190.163 100.412 277.745 36.141l131.313 135.53c13.433 13.854 35.238 13.854 48.67 0a36.322 36.322 0 0 0 0-50.237z m-299.67-116.857c-65.657 0-122.278-47.947-135.048-114.447-12.83-66.56 21.685-133.12 82.402-159.081 60.657-25.962 130.71-4.036 167.153 52.344a145.589 145.589 0 0 1-17.107 179.501 134.686 134.686 0 0 1-97.4 41.623z m103.303-497.302c18.974 0 34.334-15.963 34.334-35.539a35.057 35.057 0 0 0-34.334-35.66l-550.791-0.18a34.997 34.997 0 0 0-34.394 35.539c0 19.636 15.36 35.66 34.334 35.66l550.791 0.18zM442.971 533.083a34.214 34.214 0 0 0 29.816-17.77 36.503 36.503 0 0 0 0-35.478 34.214 34.214 0 0 0-29.816-17.77H236.484a34.936 34.936 0 0 0-34.334 35.54 34.936 34.936 0 0 0 34.334 35.417l206.487 0.06zM236.484 675.057a34.936 34.936 0 0 0-34.334 35.539c0 19.576 15.36 35.479 34.334 35.479h137.698a34.153 34.153 0 0 0 30.057-17.59 36.442 36.442 0 0 0 0-35.719 34.153 34.153 0 0 0-30.117-17.588l-137.638-0.12z" p-id="1133"></path></svg>
src/icons/svg/standard-params.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1749111250357" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="939" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M748.8 128h121.6c12.8 0 25.6 12.8 25.6 25.6v774.4c0 12.8-12.8 25.6-25.6 25.6H153.6c-12.8 0-25.6-12.8-25.6-25.6V153.6c0-12.8 12.8-25.6 25.6-25.6h121.6V64H153.6C102.4 64 64 102.4 64 153.6v774.4c0 51.2 38.4 89.6 89.6 89.6h710.4c51.2 0 89.6-38.4 89.6-89.6V153.6c6.4-51.2-32-89.6-83.2-89.6h-121.6v64z" p-id="940"></path><path d="M320 0h384c38.4 0 64 25.6 64 64v64c0 38.4-25.6 64-64 64H320c-38.4 0-64-25.6-64-64V64c0-38.4 25.6-64 64-64z m0 64v64h384V64H320zM544 448h192c19.2 0 32 12.8 32 32s-12.8 32-32 32h-192c-19.2 0-32-12.8-32-32s12.8-32 32-32zM544 640h192c19.2 0 32 12.8 32 32s-12.8 32-32 32h-192c-19.2 0-32-12.8-32-32s12.8-32 32-32zM544 320h192c19.2 0 32 12.8 32 32s-12.8 32-32 32h-192c-19.2 0-32-12.8-32-32s12.8-32 32-32zM544 768h192c19.2 0 32 12.8 32 32s-12.8 32-32 32h-192c-19.2 0-32-12.8-32-32s12.8-32 32-32zM320 320h64c38.4 0 64 25.6 64 64v64c0 38.4-25.6 64-64 64H320c-38.4 0-64-25.6-64-64V384c0-38.4 25.6-64 64-64z m0 64v64h64V384H320zM320 640h64c38.4 0 64 25.6 64 64v64c0 38.4-25.6 64-64 64H320c-38.4 0-64-25.6-64-64v-64c0-38.4 25.6-64 64-64z m0 64v64h64v-64H320z" p-id="941"></path></svg>
src/layout/components/Navbar.vue
@@ -36,7 +36,7 @@
      :close-on-click-modal="false"
      top="0"
      class="dialog-center"
      :modal-append-to-body="false"
      :append-to-body="true"
    >
      <pwd-change v-if="pwdVisible" @success="pwdVisible = false"></pwd-change>
    </el-dialog>
src/main.js
@@ -18,6 +18,7 @@
import { registerGlobalComponents } from './globalComponents.js';
import '@/utils/common.js';
import '@/utils/echartsEvent.js';
const app = createApp(App);
// const pinia = createPinia();
src/router/index.js
@@ -7,6 +7,7 @@
import datasRouter from './modules/datas';
import alarmRouter from './modules/alarm';
import statisticsRouter from './modules/statistics';
import dcPowerStatus from "@/router/modules/dcPowerStatus.js";
/* Layout */
const Layout = () => import('@/layout/index.vue');
@@ -83,6 +84,7 @@
  datasRouter,
  alarmRouter,
  statisticsRouter,
  dcPowerStatus,
  // 404 page must be placed at the end !!!
  { path: '/:pathMatch(.*)*', redirect: '/404', meta: { hidden: true }}
];
src/router/modules/alarm.js
@@ -17,6 +17,12 @@
      meta: { title: '电池实时告警', icon: 'alarm', noCache: false }
    },
    {
      path: 'batt-his',
      component: () => import('@/views/alarm/battHisAlarm.vue'),
      name: 'battHisAlarm',
      meta: { title: '电池历史告警', icon: 'alarm', noCache: false }
    },
    {
      path: 'dev',
      component: () => import('@/views/alarm/devAlarm.vue'),
      name: 'devAlarm',
src/router/modules/datas.js
@@ -17,6 +17,12 @@
      meta: { title: '实时监测', icon: 'realtime1', noCache: false, decoration: false }
    },
    {
      path: 'history',
      component: () => import('@/views/history/index.vue'),
      name: 'history',
      meta: { title: '历史数据', icon: 'history', noCache: false, decoration: false }
    },
    {
      path: 'device',
      component: () => import('@/views/datas/device.vue'),
      name: 'device',
src/router/modules/dcPowerStatus.js
New file
@@ -0,0 +1,28 @@
const Layout = () => import('@/layout/index.vue');
const dcPowerStatusRouter = {
  path: '/dcPowerStatus',
  component: Layout,
  redirect: 'noRedirect',
  name: 'DcPowerStatus',
  meta: {
    title: '直流电源运行状态分析',
    icon: 'dc-power'
  },
  children: [
    {
      path: 'standard-params',
      component: () => import('@/views/dcPowerStatus/standardParams.vue'),
      name: 'StandardParams',
      meta: { title: '标准参数管理', icon: 'standard-params', noCache: false }
    },
    {
      path: 'realtime-data-line',
      component: () => import('@/views/dcPowerStatus/standardParams.vue'),
      name: 'RealtimeDataLine',
      meta: { title: '实时数据曲线统计', icon: 'data-line', noCache: false }
    },
  ]
};
export default dcPowerStatusRouter;
src/router/modules/statistics.js
@@ -10,17 +10,29 @@
    icon: 'tongji'
  },
  children: [
    // {
    //   path: 'power',
    //   component: () => import('@/views/statistics/power.vue'),
    //   name: 'power',
    //   meta: { title: '电源统计', icon: 'component', noCache: false }
    // },
    {
      path: 'power',
      component: () => import('@/views/statistics/power.vue'),
      name: 'power',
      meta: { title: '电源信息统计', icon: 'component', noCache: false }
    },
    {
      path: 'batt',
      component: () => import('@/views/statistics/batt.vue'),
      name: 'batt',
      meta: { title: '电池组信息统计', icon: 'component', noCache: false }
    },
    {
      path: 'dev-workstatus',
      component: () => import('@/views/statistics/devWorkstatus.vue'),
      name: 'devWorkstatus',
      meta: { title: '设备工作状态统计', icon: 'workstatus', noCache: false }
    },
    {
      path: 'device',
      component: () => import('@/views/statistics/device.vue'),
      name: 'deviceStatistics',
      meta: { title: '设备信息统计', icon: 'dev1', noCache: false }
    },
    {
      path: 'batt-hr',
@@ -29,11 +41,30 @@
      meta: { title: '蓄电池核容信息统计', icon: 'component', noCache: false }
    },
    {
      path: 'batt-cell',
      component: () => import('@/views/statistics/battCell.vue'),
      name: 'battCell',
      meta: { title: '电节数量统计', icon: 'component', noCache: false }
    },
    {
      path: 'station',
      component: () => import('@/views/statistics/station.vue'),
      name: 'station',
      meta: { title: '站点统计', icon: 'stations', noCache: false }
    },
    /////// ==================================================
    {
      path: 'batt-compare0',
      component: () => import('@/views/statistics/battCompare0.vue'),
      name: 'battCompare0',
      meta: { title: '蓄电池组对比分析1', icon: 'component', noCache: false, hidden: true }
    },
    {
      path: 'batt-compare1',
      component: () => import('@/views/statistics/battCompare1.vue'),
      name: 'battCompare1',
      meta: { title: '蓄电池组对比分析2', icon: 'component', noCache: false, hidden: true }
    },
  ]
};
src/styles/blue.css
@@ -217,6 +217,7 @@
}
.page-filter {
  display: table;
  background: #071426;
  border-bottom: 1px solid #304a75;
  margin-top: 8px;
  padding: 6px;
@@ -265,6 +266,7 @@
  gap: 12px;
  /* 列之间的间距 */
  padding: 10px;
  align-items: center;
}
.page-filter .grid-container .grid-item {
  color: #48a5d0;
src/styles/blue.less
@@ -274,6 +274,7 @@
// 搜索条件样式
.page-filter {
  display: table;
  background: #071426;
  border-bottom: 1px solid #304a75;
  margin-top: 8px;
  padding: 6px;
@@ -332,6 +333,7 @@
    gap: 12px; /* 列之间的间距 */
    padding: 10px;
    align-items: center;
    .grid-item {
      color: #48a5d0;
      //     display: flex;
src/styles/index.css
@@ -173,7 +173,7 @@
  --light-color: #00feff;
  --bg-color: #072d44;
  --el-text-color-placeholder: #4ba1fa;
  --el-fill-color-lighter: #1F3C64;
  --el-fill-color-lighter: #1f3c64;
  --border-light-color: #143a92;
  --filter-input-border-color: rgba(255, 227, 41, 0.2);
  --el-dialog-bg-color: var(--bg-color);
@@ -222,7 +222,7 @@
  --el-tree-node-content-height: 26px;
  --el-tree-node-hover-bg-color: #214865;
  --el-tree-text-color: var(--el-text-color-regular);
  --el-tree-expand-icon-color: #1FCBE1;
  --el-tree-expand-icon-color: #1fcbe1;
  background: none;
  color: #ffffff;
  cursor: default;
@@ -245,10 +245,10 @@
  --el-table-header-text-color: #fff;
  --el-table-row-hover-bg-color: #1a5a8b;
  --el-table-current-row-bg-color: var(--el-color-primary-light-9);
  --el-table-header-bg-color: #1F3C64;
  --el-table-header-bg-color: #1f3c64;
  --el-table-fixed-box-shadow: var(--el-box-shadow-light);
  --el-table-bg-color: var(--bg-color);
  --el-table-tr-bg-color: #0D2B4D;
  --el-table-tr-bg-color: #0d2b4d;
  --el-table-expanded-cell-bg-color: var(--el-fill-color-blank);
  --el-table-index: var(--el-index-normal);
  background-color: var(--bg-color);
@@ -293,10 +293,11 @@
  background: transparent;
}
html.blue-theme .ys-title {
  font-family: 'YouSheBiaoTiHei';
  font-family: "YouSheBiaoTiHei";
}
html.blue-theme .panel-title {
  font-family: 'YouSheBiaoTiHei';
  font-family: "YouSheBiaoTiHei";
  font-size: 24px;
  padding-left: 1.4em;
  background: url("@/assets/images/tb1.png") 6px center / auto 82% no-repeat;
}
@@ -304,16 +305,16 @@
  color: #1fcbe1;
}
.el-tabs--border-card > .el-tabs__header.el-tabs__header {
  border-bottom: 1px solid #00fefe;
  border-bottom: 1px solid #1c4975;
  background-color: #00243e;
}
.el-tabs--border-card > .el-tabs__header.el-tabs__header .el-tabs__item {
  color: #00fefe;
  color: #fff;
}
.el-tabs--border-card > .el-tabs__header.el-tabs__header .el-tabs__item.is-active {
  background-color: #00fefe;
  border-color: #00fefe;
  color: #041f6c;
  background-color: #014886;
  border-color: #014886;
  color: #fff;
}
.el-pagination__sizes.el-pagination__sizes,
.el-pagination__total.el-pagination__total {
@@ -321,9 +322,9 @@
  font-size: 16px;
}
.el-pagination .el-select__wrapper.el-select__wrapper {
  background-color: #134BA8;
  background-color: #134ba8;
  color: #fff;
  box-shadow: 0 0 0 1px #436DA8 inset;
  box-shadow: 0 0 0 1px #436da8 inset;
}
.el-pagination .el-select__wrapper.el-select__wrapper .el-select__placeholder,
.el-pagination .el-select__wrapper.el-select__wrapper .el-select__input {
@@ -351,8 +352,8 @@
  color: #fff;
}
.el-pagination__jump.el-pagination__jump .el-input__wrapper {
  background-color: #134BA8;
  box-shadow: 0 0 0 1px #436DA8 inset;
  background-color: #134ba8;
  box-shadow: 0 0 0 1px #436da8 inset;
}
.el-pagination__jump.el-pagination__jump .el-input__wrapper .el-input__inner {
  color: #fff;
@@ -373,8 +374,8 @@
}
.el-transfer .el-checkbox__input.is-indeterminate .el-checkbox__inner,
.el-transfer .el-checkbox__input.is-checked .el-checkbox__inner {
  background-color: #46CAFE;
  border-color: #46CAFE;
  background-color: #46cafe;
  border-color: #46cafe;
}
.el-transfer .el-checkbox__input.is-indeterminate .el-checkbox__inner::before,
.el-transfer .el-checkbox__input.is-checked .el-checkbox__inner::before {
@@ -386,14 +387,14 @@
}
.page-filter {
  display: table;
  border-bottom: 1px solid #304A75;
  border-bottom: 1px solid #304a75;
  margin-top: 8px;
  padding: 6px;
  margin-bottom: 6px;
}
.page-filter .el-select__wrapper.el-select__wrapper {
  background-color: #103047;
  color: #48A5D0;
  color: #48a5d0;
  box-shadow: 0 0 0 1px #103047 inset;
}
.page-filter .el-select__placeholder {
@@ -401,15 +402,55 @@
}
.page-filter .table-row {
  display: table-row;
  table-layout: fixed;
}
.page-filter .table-row .table-cell {
  display: table-cell;
  white-space: nowrap;
  color: #48A5D0;
  color: #48a5d0;
  font-size: 16px;
}
.page-filter .table-cell.text-right {
  text-align: right;
  padding-right: 10px;
}
.page-filter .el-date-editor .el-input__wrapper {
  align-items: center;
  background-color: #103047;
  background-image: none;
  border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
  box-shadow: 0 0 0 1px #103047 inset;
  cursor: text;
  display: inline-flex;
  flex-grow: 1;
  justify-content: center;
  padding: 1px 11px;
}
.page-filter .el-date-editor .el-input__wrapper .el-input__inner {
  color: #48a5d0;
}
.page-filter .grid-container {
  display: grid;
  grid-template-columns: repeat(var(--counter), auto 1fr);
  gap: 12px;
  /* 列之间的间距 */
  padding: 10px;
}
.page-filter .grid-container .grid-item {
  color: #48a5d0;
  display: contents;
  /* 使项目内容直接参与网格布局 */
}
.page-filter .grid-container .grid-item .label {
  white-space: nowrap;
  margin-right: -6px;
  margin-left: 1em;
  color: #48a5d0;
  font-size: 16px;
  text-align: right;
}
.page-filter .grid-container .grid-item .label::after {
  content: ":";
}
.pos-r {
  position: relative;
@@ -605,7 +646,7 @@
  left: 0;
  right: 0;
  bottom: 0;
  background-image: url("@/assets/images/dw_bg.jpg");
  background: #071426;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  z-index: 9999;
src/styles/index.less
@@ -223,7 +223,8 @@
      left: 0;
      right: 0;
      bottom: 0;
      background-image: url("@/assets/images/dw_bg.jpg");
      // background-image: url("@/assets/images/dw_bg.jpg");
      background: #071426;
      background-repeat: no-repeat;
      background-size: 100% 100%;
      z-index: 9999;
src/utils/echartsEvent.js
New file
@@ -0,0 +1,16 @@
import * as echarts from 'echarts';
import addPassiveEventListener from './passiveEventListener';
// 原始 ECharts 的事件监听方法
const originalAddListener = echarts.util.addDomListener;
// 覆盖 addDomListener 方法,强制使用被动监听
echarts.util.addDomListener  = function(element, type, handler, capture) {
  // 仅对滚动相关事件(wheel/mousewheel)生效
  if (['wheel', 'mousewheel'].includes(type)) {
    // console.log('addPassiveEventListener', element, type, '=============');
    addPassiveEventListener(element, type, handler);
  } else {
    originalAddListener.call(this,  element, type, handler, capture);
  }
};
src/utils/formatSeconds.js
@@ -6,6 +6,10 @@
 * @return  {[String]}         00:00:00
 */
function formatSeconds(value) {
    if (isNaN(Number(value))) {
      return value;
    }
    if(value<=0){
        value = 0;
    }
src/utils/passiveEventListener.js
New file
@@ -0,0 +1,25 @@
/**
 * 全局被动事件监听器封装
 * @param {Element} element 目标元素
 * @param {string} eventType 事件类型(如 'wheel'、'mousewheel')
 * @param {Function} handler 事件处理函数
 */
export default function addPassiveEventListener(element, eventType, handler) {
  // 检测浏览器是否支持 passive 选项
  let supportsPassive = false;
  try {
    const opts = Object.defineProperty({},  'passive', {
      get() {
        supportsPassive = true;
      }
    });
    window.addEventListener('test',  null, opts);
  } catch (e) {}
  // 添加事件监听器,强制使用 passive: true
  element.addEventListener(
    eventType,
    handler,
    supportsPassive ? { passive: true } : false
  );
}
src/views/alarm/battAlarm.vue
@@ -29,6 +29,11 @@
    confirmBattAlm,
  } from "@/api/alarm.js";
  import {
    getBattAlarmIdType,
  } from "@/api/station.js";
    const { $loading, $message, $confirm } = useElement();
  const alarmLevel = ref();
@@ -83,7 +88,7 @@
            width: "120",
        },
    {
            prop: "almId",
            prop: "almName",
            label: "告警类型",
            width: "120",
        },
@@ -129,15 +134,26 @@
    const currentAreaId = ref();
    const currentAreaIds = ref([]);
    const datas = reactive({
        tableData: [],
    tableData: [],
        rowData: {},
    });
    // const tableData = reactive([]);
    // const rowData = reactive({});
  const almIds = ref([]);
  const alarmTypeList = ref([]);
    // const userStore = useUserStore();
    // const { uid, uname } = storeToRefs(userStore);
  const checkAll = ref(true);
  const isIndeterminate = ref(false);
  function handleCheckAllChange (val) {
    almIds.value = val ? alarmTypeList.value.map(v => v.value) : []
    isIndeterminate.value = false
  }
  function handleCheckedChange (value) {
    const checkedCount = value.length
    checkAll.value = checkedCount === alarmTypeList.value.length
    isIndeterminate.value = checkedCount > 0 && checkedCount < alarmTypeList.value.length
  }
  // almIds: [119001],
  // almLevel: "1",
@@ -149,7 +165,7 @@
  // stationName: "测试机房6",
  function sendMessage() {
    let params = {
      // almIds: [119001],
      almIds: almIds.value,
      almLevel: alarmLevel.value || undefined,
      provice: provice.value || undefined,
      city: city.value || undefined,
@@ -165,6 +181,18 @@
    nextTick(() => {
      sendMessage();
    });
  }
  // 查询告警类型
  async function getAlarmType() {
    let res = await getBattAlarmIdType();
    let { code, data, data2 } = res;
    let list = [];
    if (code && data) {
      list = Object.keys(data2).map((key) => ({value: key, label: data2[key]}));
    }
    alarmTypeList.value = list;
    almIds.value = list.map(v => v.value);
  }
  watch(
@@ -256,7 +284,8 @@
  }
    onMounted(() => {
    onMounted(async () => {
    await getAlarmType();
        sendMessage();
    });
</script>
@@ -269,96 +298,125 @@
      <yc-card is-full>
        <div class="page-content-wrapper">
          <div class="page-content-tools page-filter">
            <div class="table-row">
              <div class="table-cell text-right">省:</div>
              <div class="table-cell">
                <el-select
                  v-model="provice"
                  size="small"
                  clearable
                  placeholder="请选择省"
                  @change="selectChange"
                >
                  <el-option
                    v-for="item in proviceList"
                    :key="'l0_' + item"
                    :label="item"
                    :value="item"
            <div class="grid-container" :style="{'--counter': 5}">
              <div class="grid-item">
                <div class="label">省</div>
                <div class="value">
                  <el-select
                    v-model="provice"
                    size="small"
                    clearable
                    placeholder="请选择省"
                    @change="selectChange"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in proviceList"
                      :key="'l0_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="table-cell text-right">市:</div>
              <div class="table-cell">
                <el-select
                  v-model="city"
                  size="small"
                  clearable
                  placeholder="请选择市"
                  @change="selectChange"
                >
                  <el-option
                    v-for="item in cityList"
                    :key="'l1_' + item"
                    :label="item"
                    :value="item"
              <div class="grid-item">
                <div class="label">市</div>
                <div class="value">
                  <el-select
                    v-model="city"
                    size="small"
                    clearable
                    placeholder="请选择市"
                    @change="selectChange"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in cityList"
                      :key="'l1_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="table-cell text-right">区县:</div>
              <div class="table-cell">
                <el-select
                  v-model="country"
                  clearable
                  size="small"
                  placeholder="请选择区县"
                  @change="selectChange"
                >
                  <el-option
                    v-for="item in countryList"
                    :key="'l2_' + item"
                    :label="item"
                    :value="item"
              <div class="grid-item">
                <div class="label">区县</div>
                <div class="value">
                  <el-select
                    v-model="country"
                    clearable
                    size="small"
                    placeholder="请选择区县"
                    @change="selectChange"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in countryList"
                      :key="'l2_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="table-cell text-right">站点:</div>
              <div class="table-cell">
                <el-select
                  v-model="stationName"
                  clearable
                  size="small"
                  placeholder="请选择站点"
                  @change="selectChange"
                >
                  <el-option
                    v-for="item in stationList"
                    :key="'l3_' + item.stationId"
                    :label="item.stationName"
                    :value="item.stationName"
              <div class="grid-item">
                <div class="label">站点</div>
                <div class="value">
                  <el-select
                    v-model="stationName"
                    clearable
                    size="small"
                    placeholder="请选择站点"
                    @change="selectChange"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in stationList"
                      :key="'l3_' + item.stationId"
                      :label="item.stationName"
                      :value="item.stationName"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="table-cell text-right">告警等级:</div>
              <div class="table-cell">
                <el-select
                  v-model="alarmLevel"
                  clearable
                  size="small"
                  placeholder="请选择告警等级"
                  @change="selectChange"
                >
                  <el-option
                    v-for="item in alarmLevels"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
              <div class="grid-item">
                <div class="label">告警等级</div>
                <div class="value">
                  <el-select
                    v-model="alarmLevel"
                    clearable
                    size="small"
                    placeholder="请选择告警等级"
                    @change="selectChange"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in alarmLevels"
                      :key="item.value"
                      :label="item.label"
                      :value="item.value"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">告警类型</div>
                <div class="value flex-row" style="grid-column: span 9;">
                  <el-checkbox
                    v-model="checkAll"
                    :indeterminate="isIndeterminate"
                    @change="handleCheckAllChange"
                  >全选
                  </el-checkbox>
                  <el-checkbox-group
                    class="group"
                    v-model="almIds"
                    @change="handleCheckedChange"
                  >
                    <el-checkbox v-for="(item, idx) in alarmTypeList" :key="'list0_' + idx" :label="item.label" :value="item.value">
                    </el-checkbox>
                  </el-checkbox-group>
                </div>
              </div>
            </div>
          </div>
@@ -434,6 +492,17 @@
  .page-content-tools {
    padding-bottom: 8px;
    .flex-row {
      display: flex;
      align-items: center;
      :deep(.el-checkbox) {
        color: #fff;
      }
      .group {
        margin-left: 30px;
      }
    }
  }
  .page-content-table {
src/views/alarm/battHisAlarm.vue
New file
@@ -0,0 +1,602 @@
<script setup name="battHisAlarm">
    import { ref, reactive, onMounted, computed, nextTick, watch, } from "vue";
    import { storeToRefs } from "pinia";
    import { Search, Plus } from "@element-plus/icons-vue";
    import ycCard from "@/components/ycCard/index.vue";
    // import addEdit from "./addEdit.vue";
    import { ElMessage } from "element-plus";
    import useElement from "@/hooks/useElement.js";
  import { useUserStore } from '@/store/user';
  import useStation from "@/hooks/useStationList.js";
  const { provice, city, country, stationName,
    proviceList, cityList, countryList, stationList,
  } = useStation();
  import powerTypes from '@/utils/const/const_powerType.js';
  import { ExportFile } from '@/utils/exportFile.js';
  import { useRouter } from "vue-router";
  const router = useRouter();
  const userStore = useUserStore();
  const { uid, uname } = storeToRefs(userStore);
  import moment from 'moment';
  import {
    confirmBattAlm,
    getBattAlmHis,
  } from "@/api/alarm.js";
  import {
    getBattAlarmIdType,
  } from "@/api/station.js";
    const { $loading, $message, $confirm } = useElement();
  const alarmLevel = ref();
  const alarmLevels = [
    {
      label: '一级告警',
      value: 1,
    },
    {
      label: '二级告警',
      value: 2,
    },
    {
      label: '三级告警',
      value: 3,
    },
    {
      label: '四级告警',
      value: 4,
    },
  ];
const headers = [
    // {
        //     prop: "provice",
        //     label: "省",
        //     width: "80",
        // },
    // {
        //     prop: "city",
        //     label: "市",
        //     width: "80",
        // },
    // {
        //     prop: "country",
        //     label: "区县",
        //     width: "80",
        // },
        {
            prop: "stationName",
            label: "机房名称",
            width: "160",
        },
    // {
        //     prop: "stationType",
        //     label: "电压等级",
        //     width: "80",
        // },
    {
            prop: "battgroupName",
            label: "电池组名称",
            width: "120",
        },
    {
            prop: "almName",
            label: "告警类型",
            width: "120",
        },
    {
            prop: "almValue",
            label: "告警值",
            width: "120",
        },
    {
            prop: "monNum",
            label: "单体编号",
            width: "120",
        },
    {
            prop: "almLevelStr",
            label: "告警等级",
            width: "120",
        },
    {
            prop: "almStartTime",
            label: "告警开始时间",
            width: "120",
        },
    {
            prop: "almIsConfirmed",
            label: "告警是否确认",
            width: "120",
        },
    {
      prop: "almConfirmedTime",
      label: "告警确认时间",
      width: "120",
    },
    ];
    const background = ref(true);
    const disabled = ref(false);
    const pageCurr = ref(1);
    const pageSize = ref(10);
    const total = ref(0);
    const addEditVisible = ref(false);
    const dialogTitle = ref("");
    const currentAreaId = ref();
    const currentAreaIds = ref([]);
    const datas = reactive({
    tableData: [],
        rowData: {},
    });
  const almIds = ref([]);
  const alarmTypeList = ref([]);
  const checkAll = ref(true);
  const isIndeterminate = ref(false);
  function handleCheckAllChange (val) {
    almIds.value = val ? alarmTypeList.value.map(v => v.value) : []
    isIndeterminate.value = false
  }
  function handleCheckedChange (value) {
    const checkedCount = value.length
    checkAll.value = checkedCount === alarmTypeList.value.length
    isIndeterminate.value = checkedCount > 0 && checkedCount < alarmTypeList.value.length
  }
  const _startDate = '2025-01-01 00:00:00';
  const _endDate = moment().format('YYYY-MM-DD');
  const almStartTime = ref(moment().subtract(30, 'day').format('YYYY-MM-DD'));
  const almEndTime = ref(_endDate);
  const startDisabledDate = (time) =>  almEndTime.value ? moment(almEndTime.value).isBefore(time) || moment().isBefore(time) : moment().isBefore(time);
  const endDisabledDate = (time) => almStartTime.value ? moment(time).isBefore(almStartTime.value) || moment().isBefore(time) : moment().isBefore(time);
  // almIds: [119001],
  // almLevel: "1",
  // city: "武汉市",
  // country: "东西湖区",
  // pageNum: 1,
  // pageSize: 10,
  // provice: "湖北省",
  // stationName: "测试机房6",
  function getList() {
    let params = {
      almIds: almIds.value,
      almLevel: alarmLevel.value || undefined,
      provice: provice.value || undefined,
      city: city.value || undefined,
      country: country.value || undefined,
      stationName: stationName.value || undefined,
      almStartTime: almStartTime.value ? almStartTime.value + ' 00:00:00' : _startDate,
      almEndTime: almEndTime.value ? almEndTime.value + ' 23:59:59' : _endDate + ' 23:59:59',
      pageNum: pageCurr.value,
      pageSize: pageSize.value,
    };
    getBattAlmHis(params).then((res) => {
      let { code, data, data2 } = res;
      let list = [];
      let _total = 0;
      if (code && data) {
        // console.log(data);
        list = data2.list.map(v => ({
          ...v,
          almLevelStr: ['', '一级告警', '二级告警', '三级告警', '四级告警'][v.almLevel],
          almIsConfirmedStr: ['未确认', '已确认'][v.almIsConfirmed],
          // roleName: roles[v.role],
        }));
        _total = data2.total;
      }
      datas.tableData = list;
      total.value = _total;
    })
    .catch((err) => {
      console.log(err);
    });
  }
  function selectChange() {
    nextTick(() => {
      getList();
    });
  }
  // 查询告警类型
  async function getAlarmType() {
    let res = await getBattAlarmIdType();
    let { code, data, data2 } = res;
    let list = [];
    if (code && data) {
      list = Object.keys(data2).map((key) => ({value: key, label: data2[key]}));
    }
    alarmTypeList.value = list;
    almIds.value = list.map(v => v.value);
  }
    // 展示数据数量
    function handleSizeChange(val) {
        pageSize.value = val;
        getList();
    }
    // 翻页
    function handleCurrentChange(val) {
        pageCurr.value = val;
        getList();
    }
  function confirmAlarm(record) {
    $confirm("确认告警", () => {
      let loading = $loading();
      confirmBattAlm(record.num)
        .then((res) => {
          let { code, data } = res;
          loading.close();
          if (code && data) {
            $message.success("操作成功");
            getList();
          } else {
            $message.success("操作失败");
          }
        })
        .catch((err) => {
          loading.close();
          console.log(err);
        });
    });
  }
    function onOk() {
        addEditVisible.value = false;
        handleCurrentChange(1);
    }
    function onCanel() {
        addEditVisible.value = false;
    }
  function exportExcel() {
    let _headers = headers.map(v => {
      let prop = v.prop;
      let label = v.label;
      if (prop == 'almIsConfirmed') {
        prop = 'almIsConfirmedStr';
      }
      return {
        prop,
        label
      };
    });
    ExportFile(_headers, datas.tableData, "电池实时告警");
  }
  function goRt (row) {
    router.push({
      path: '/datas/realtime',
      query: {
        id: row.battgroupId
      }
    });
  }
    onMounted(async () => {
    await getAlarmType();
        getList();
    });
</script>
<template>
  <div class="page-wrapper">
    <!-- <div class="page-header">
    </div> -->
    <div class="page-content">
      <yc-card is-full>
        <div class="page-content-wrapper">
          <div class="page-content-tools page-filter">
            <div class="grid-container" :style="{'--counter': 8}">
              <div class="grid-item">
                <div class="label">省</div>
                <div class="value">
                  <el-select
                    v-model="provice"
                    size="small"
                    clearable
                    placeholder="请选择省"
                    @change="selectChange"
                  >
                    <el-option
                      v-for="item in proviceList"
                      :key="'l0_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">市</div>
                <div class="value">
                  <el-select
                    v-model="city"
                    size="small"
                    clearable
                    placeholder="请选择市"
                    @change="selectChange"
                  >
                    <el-option
                      v-for="item in cityList"
                      :key="'l1_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">区县</div>
                <div class="value">
                  <el-select
                    v-model="country"
                    clearable
                    size="small"
                    placeholder="请选择区县"
                    @change="selectChange"
                  >
                    <el-option
                      v-for="item in countryList"
                      :key="'l2_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">站点</div>
                <div class="value">
                  <el-select
                    v-model="stationName"
                    clearable
                    size="small"
                    placeholder="请选择站点"
                    @change="selectChange"
                  >
                    <el-option
                      v-for="item in stationList"
                      :key="'l3_' + item.stationId"
                      :label="item.stationName"
                      :value="item.stationName"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">告警等级</div>
                <div class="value">
                  <el-select
                    v-model="alarmLevel"
                    clearable
                    size="small"
                    placeholder="请选择告警等级"
                    @change="selectChange"
                  >
                    <el-option
                      v-for="item in alarmLevels"
                      :key="item.value"
                      :label="item.label"
                      :value="item.value"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">测试开始时间</div>
                <div class="value" style="grid-column: span 5;">
                  <el-date-picker
                    v-model="almStartTime"
                    type="date"
                    size="small"
                    placeholder="选择日期"
                    format="YYYY-MM-DD"
                    value-format="YYYY-MM-DD"
                    @change="selectChange"
                    :disabled-date="startDisabledDate"
                  />
                  -
                  <el-date-picker
                    v-model="almEndTime"
                    size="small"
                    type="date"
                    placeholder="选择日期"
                    format="YYYY-MM-DD"
                    value-format="YYYY-MM-DD"
                    @change="selectChange"
                    :disabled-date="endDisabledDate"
                  />
                </div>
              </div>
              <div class="grid-item">
                <div class="label">告警类型</div>
                <div class="value flex-row" style="grid-column: span 15;">
                  <el-checkbox
                    v-model="checkAll"
                    :indeterminate="isIndeterminate"
                    @change="handleCheckAllChange"
                  >全选
                  </el-checkbox>
                  <el-checkbox-group
                    class="group"
                    v-model="almIds"
                    @change="handleCheckedChange"
                  >
                    <el-checkbox v-for="(item, idx) in alarmTypeList" :key="'list0_' + idx" :label="item.label" :value="item.value">
                    </el-checkbox>
                  </el-checkbox-group>
                </div>
              </div>
            </div>
          </div>
          <div class="page-content-table">
            <div class="pos-rel">
              <div class="pos-abs">
                <el-table class="yc-table" stripe height="100%" size="small" :data="datas.tableData" style="width: 100%">
                  <el-table-column type="index" fixed="left" label="序号" width="60"></el-table-column>
                  <el-table-column v-for="header in headers" :key="header.prop" :prop="header.prop" :label="header.label"
                    :min-width="header.width" align="center">
                    <template #default="scope">
                      <template v-if="header.prop == 'almIsConfirmed'">
                        <el-checkbox disabled :checked="scope.row[header.prop] == 1"></el-checkbox>
                      </template>
                      <template v-else>
                        {{ scope.row[header.prop] || '--' }}
                      </template>
                    </template>
                  </el-table-column>
                  <el-table-column label="操作" fixed="right" width="240" align="center">
                    <template #default="scope">
                      <el-button type="primary" size="small" v-if="!scope.row.almIsConfirmed"
                        @click="confirmAlarm(scope.row)">确认告警</el-button>
                      <el-button type="warning" size="small" @click="goRt(scope.row)">实时监测</el-button>
                    </template>
                  </el-table-column>
                </el-table>
              </div>
            </div>
          </div>
          <div class="page-content-page">
            <div class="page-tool">
              <el-button type="primary" round size="small" @click="getList" :icon="Search">查询</el-button>
            </div>
            <div class="el-page-container">
              <el-pagination v-model:current-page="pageCurr" v-model:page-size="pageSize"
                :page-sizes="[20, 40, 60, 80, 100, 200, 300, 400]" size="small" :disabled="disabled"
                :background="background" layout="total, sizes, prev, pager, next, jumper" :total="total"
                @size-change="handleSizeChange" @current-change="handleCurrentChange" />
            </div>
            <div class="page-tool">
              <el-button type="primary" round size="small" @click="exportExcel" >导出</el-button>
            </div>
          </div>
        </div>
      </yc-card>
    </div>
    <div class="page-footer"></div>
    <!-- 弹窗 -->
    <!-- <el-dialog :title="dialogTitle" v-model="addEditVisible" top="0" :close-on-click-modal="false" class="dialog-center"
      width="860px" center>
      <add-edit v-if="addEditVisible" @success="onOk" :info="datas.rowData" @cancel="onCanel"></add-edit>
    </el-dialog> -->
  </div>
</template>
<style scoped lang="less">
.page-wrapper {
  display: flex;
  flex-direction: row;
  // padding: 8px;
  height: 100%;
  .page-content {
    flex: 1;
  }
}
.page-content-wrapper {
  display: flex;
  flex-direction: column;
  height: 100%;
  .page-content-tools {
    padding-bottom: 8px;
    .flex-row {
      display: flex;
      align-items: center;
      :deep(.el-checkbox) {
        color: #fff;
      }
      .group {
        margin-left: 30px;
      }
    }
  }
  .page-content-table {
    // border-top: 1px solid var(--border-light-color);
    box-sizing: border-box;
    flex: 1;
    margin-left: 26px;
    margin-right: 26px;
  }
  .page-content-page {
    padding: 8px 8px 0 8px;
    text-align: center;
    .el-page-container {
      display: inline-block;
      padding: 0 16px;
    }
    .page-tool {
      display: inline-block;
    }
  }
}
.hdw-card-container {
  width: 240px;
  padding-right: 8px;
  height: 100%;
}
.pos-rel {
  position: relative;
  width: 100%;
  height: 100%;
  .pos-abs {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    height: 100%;
  }
}
.tools-filter {
  display: inline-block;
  font-size: 14px;
  .tools-filter-item {
    display: inline-block;
    margin-right: 8px;
    .filter-label {
      display: inline-block;
    }
    .filter-content {
      display: inline-block;
    }
  }
}
</style>
src/views/alarm/devAlarm.vue
@@ -27,6 +27,10 @@
  import {
    confirmDevAlm,
  } from "@/api/alarm.js";
  import {
    getDevAlmIdType,
  } from "@/api/station.js";
    const { $loading, $message, $confirm } = useElement();
@@ -127,11 +131,38 @@
        rowData: {},
    });
  const almIds = ref([]);
  const alarmTypeList = ref([]);
    // const tableData = reactive([]);
    // const rowData = reactive({});
    // const userStore = useUserStore();
    // const { uid, uname } = storeToRefs(userStore);
  const checkAll = ref(true);
  const isIndeterminate = ref(false);
  function handleCheckAllChange (val) {
    almIds.value = val ? alarmTypeList.value.map(v => v.value) : []
    isIndeterminate.value = false
  }
  function handleCheckedChange (value) {
    const checkedCount = value.length
    checkAll.value = checkedCount === alarmTypeList.value.length
    isIndeterminate.value = checkedCount > 0 && checkedCount < alarmTypeList.value.length
  }
  // 查询告警类型
  async function getAlarmType() {
    let res = await getDevAlmIdType();
    let { code, data, data2 } = res;
    let list = [];
    if (code && data) {
      list = Object.keys(data2).map((key) => ({value: key, label: data2[key]}));
    }
    alarmTypeList.value = list;
    almIds.value = list.map(v => v.value);
  }
  // almIds: [119001],
  // almLevel: "1",
@@ -143,7 +174,7 @@
  // stationName: "测试机房6",
  function sendMessage() {
    let params = {
      // almIds: [119001],
      almIds: almIds.value,
      almLevel: alarmLevel.value || undefined,
      provice: provice.value || undefined,
      city: city.value || undefined,
@@ -250,7 +281,8 @@
  }
    onMounted(() => {
    onMounted(async () => {
    await getAlarmType();
        sendMessage();
    });
</script>
@@ -263,96 +295,125 @@
      <yc-card is-full>
        <div class="page-content-wrapper">
          <div class="page-content-tools page-filter">
            <div class="table-row">
              <div class="table-cell text-right">省:</div>
              <div class="table-cell">
                <el-select
                  v-model="provice"
                  size="small"
                  clearable
                  placeholder="请选择省"
                  @change="selectChange"
                >
                  <el-option
                    v-for="item in proviceList"
                    :key="'l0_' + item"
                    :label="item"
                    :value="item"
            <div class="grid-container" :style="{'--counter': 5}">
              <div class="grid-item">
                <div class="label">省</div>
                <div class="value">
                  <el-select
                    v-model="provice"
                    size="small"
                    clearable
                    placeholder="请选择省"
                    @change="selectChange"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in proviceList"
                      :key="'l0_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="table-cell text-right">市:</div>
              <div class="table-cell">
                <el-select
                  v-model="city"
                  size="small"
                  clearable
                  placeholder="请选择市"
                  @change="selectChange"
                >
                  <el-option
                    v-for="item in cityList"
                    :key="'l1_' + item"
                    :label="item"
                    :value="item"
              <div class="grid-item">
                <div class="label">市</div>
                <div class="value">
                  <el-select
                    v-model="city"
                    size="small"
                    clearable
                    placeholder="请选择市"
                    @change="selectChange"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in cityList"
                      :key="'l1_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="table-cell text-right">区县:</div>
              <div class="table-cell">
                <el-select
                  v-model="country"
                  clearable
                  size="small"
                  placeholder="请选择区县"
                  @change="selectChange"
                >
                  <el-option
                    v-for="item in countryList"
                    :key="'l2_' + item"
                    :label="item"
                    :value="item"
              <div class="grid-item">
                <div class="label">区县</div>
                <div class="value">
                  <el-select
                    v-model="country"
                    clearable
                    size="small"
                    placeholder="请选择区县"
                    @change="selectChange"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in countryList"
                      :key="'l2_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="table-cell text-right">站点:</div>
              <div class="table-cell">
                <el-select
                  v-model="stationName"
                  clearable
                  size="small"
                  placeholder="请选择站点"
                  @change="selectChange"
                >
                  <el-option
                    v-for="item in stationList"
                    :key="'l3_' + item.stationId"
                    :label="item.stationName"
                    :value="item.stationName"
              <div class="grid-item">
                <div class="label">站点</div>
                <div class="value">
                  <el-select
                    v-model="stationName"
                    clearable
                    size="small"
                    placeholder="请选择站点"
                    @change="selectChange"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in stationList"
                      :key="'l3_' + item.stationId"
                      :label="item.stationName"
                      :value="item.stationName"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="table-cell text-right">告警等级:</div>
              <div class="table-cell">
                <el-select
                  v-model="alarmLevel"
                  clearable
                  size="small"
                  placeholder="请选择告警等级"
                  @change="selectChange"
                >
                  <el-option
                    v-for="item in alarmLevels"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
              <div class="grid-item">
                <div class="label">告警等级</div>
                <div class="value">
                  <el-select
                    v-model="alarmLevel"
                    clearable
                    size="small"
                    placeholder="请选择告警等级"
                    @change="selectChange"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in alarmLevels"
                      :key="item.value"
                      :label="item.label"
                      :value="item.value"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">告警类型</div>
                <div class="value flex-row" style="grid-column: span 9;">
                  <el-checkbox
                    v-model="checkAll"
                    :indeterminate="isIndeterminate"
                    @change="handleCheckAllChange"
                  >全选
                  </el-checkbox>
                  <el-checkbox-group
                    class="group"
                    v-model="almIds"
                    @change="handleCheckedChange"
                  >
                    <el-checkbox v-for="(item, idx) in alarmTypeList" :key="'list0_' + idx" :label="item.label" :value="item.value">
                    </el-checkbox>
                  </el-checkbox-group>
                </div>
              </div>
            </div>
          </div>
@@ -428,6 +489,16 @@
  .page-content-tools {
    padding-bottom: 8px;
    .flex-row {
      display: flex;
      align-items: center;
      :deep(.el-checkbox) {
        color: #fff;
      }
      .group {
        margin-left: 30px;
      }
    }
  }
  .page-content-table {
src/views/alarm/pwrAlarm.vue
@@ -33,6 +33,10 @@
    } from "@/api/user";
  import {
    getPowerAlmIdType,
  } from '@/api/station.js';
  import {
    confirmPwrAlm,
  } from "@/api/alarm.js";
@@ -135,6 +139,22 @@
        rowData: {},
    });
  const almIds = ref([]);
  const alarmTypeList = ref([]);
  const checkAll = ref(true);
  const isIndeterminate = ref(false);
  function handleCheckAllChange (val) {
    almIds.value = val ? alarmTypeList.value.map(v => v.value) : []
    isIndeterminate.value = false
  }
  function handleCheckedChange (value) {
    const checkedCount = value.length
    checkAll.value = checkedCount === alarmTypeList.value.length
    isIndeterminate.value = checkedCount > 0 && checkedCount < alarmTypeList.value.length
  }
    // const tableData = reactive([]);
    // const rowData = reactive({});
@@ -167,6 +187,18 @@
    nextTick(() => {
      sendMessage();
    });
  }
  // 查询告警类型
  async function getAlarmType() {
    let res = await getPowerAlmIdType();
    let { code, data, data2 } = res;
    let list = [];
    if (code && data) {
      list = Object.keys(data2).map((key) => ({value: key, label: data2[key]}));
    }
    alarmTypeList.value = list;
    almIds.value = list.map(v => v.value);
  }
  watch(
@@ -258,7 +290,8 @@
  }
    onMounted(() => {
    onMounted(async () => {
    await getAlarmType();
        sendMessage();
    });
</script>
@@ -271,96 +304,125 @@
      <yc-card is-full>
        <div class="page-content-wrapper">
          <div class="page-content-tools page-filter">
            <div class="table-row">
              <div class="table-cell text-right">省:</div>
              <div class="table-cell">
                <el-select
                  v-model="provice"
                  size="small"
                  clearable
                  placeholder="请选择省"
                  @change="selectChange"
                >
                  <el-option
                    v-for="item in proviceList"
                    :key="'l0_' + item"
                    :label="item"
                    :value="item"
            <div class="grid-container" :style="{'--counter': 5}">
              <div class="grid-item">
                <div class="label">省</div>
                <div class="value">
                  <el-select
                    v-model="provice"
                    size="small"
                    clearable
                    placeholder="请选择省"
                    @change="selectChange"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in proviceList"
                      :key="'l0_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="table-cell text-right">市:</div>
              <div class="table-cell">
                <el-select
                  v-model="city"
                  size="small"
                  clearable
                  placeholder="请选择市"
                  @change="selectChange"
                >
                  <el-option
                    v-for="item in cityList"
                    :key="'l1_' + item"
                    :label="item"
                    :value="item"
              <div class="grid-item">
                <div class="label">市</div>
                <div class="value">
                  <el-select
                    v-model="city"
                    size="small"
                    clearable
                    placeholder="请选择市"
                    @change="selectChange"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in cityList"
                      :key="'l1_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="table-cell text-right">区县:</div>
              <div class="table-cell">
                <el-select
                  v-model="country"
                  clearable
                  size="small"
                  placeholder="请选择区县"
                  @change="selectChange"
                >
                  <el-option
                    v-for="item in countryList"
                    :key="'l2_' + item"
                    :label="item"
                    :value="item"
              <div class="grid-item">
                <div class="label">区县</div>
                <div class="value">
                  <el-select
                    v-model="country"
                    clearable
                    size="small"
                    placeholder="请选择区县"
                    @change="selectChange"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in countryList"
                      :key="'l2_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="table-cell text-right">站点:</div>
              <div class="table-cell">
                <el-select
                  v-model="stationName"
                  clearable
                  size="small"
                  placeholder="请选择站点"
                  @change="selectChange"
                >
                  <el-option
                    v-for="item in stationList"
                    :key="'l3_' + item.stationId"
                    :label="item.stationName"
                    :value="item.stationName"
              <div class="grid-item">
                <div class="label">站点</div>
                <div class="value">
                  <el-select
                    v-model="stationName"
                    clearable
                    size="small"
                    placeholder="请选择站点"
                    @change="selectChange"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in stationList"
                      :key="'l3_' + item.stationId"
                      :label="item.stationName"
                      :value="item.stationName"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="table-cell text-right">告警等级:</div>
              <div class="table-cell">
                <el-select
                  v-model="alarmLevel"
                  clearable
                  size="small"
                  placeholder="请选择告警等级"
                  @change="selectChange"
                >
                  <el-option
                    v-for="item in alarmLevels"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
              <div class="grid-item">
                <div class="label">告警等级</div>
                <div class="value">
                  <el-select
                    v-model="alarmLevel"
                    clearable
                    size="small"
                    placeholder="请选择告警等级"
                    @change="selectChange"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in alarmLevels"
                      :key="item.value"
                      :label="item.label"
                      :value="item.value"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">告警类型</div>
                <div class="value flex-row" style="grid-column: span 9;">
                  <el-checkbox
                    v-model="checkAll"
                    :indeterminate="isIndeterminate"
                    @change="handleCheckAllChange"
                  >全选
                  </el-checkbox>
                  <el-checkbox-group
                    class="group"
                    v-model="almIds"
                    @change="handleCheckedChange"
                  >
                    <el-checkbox v-for="(item, idx) in alarmTypeList" :key="'list0_' + idx" :label="item.label" :value="item.value">
                    </el-checkbox>
                  </el-checkbox-group>
                </div>
              </div>
            </div>
          </div>
@@ -436,6 +498,16 @@
  .page-content-tools {
    padding-bottom: 8px;
    .flex-row {
      display: flex;
      align-items: center;
      :deep(.el-checkbox) {
        color: #fff;
      }
      .group {
        margin-left: 30px;
      }
    }
  }
  .page-content-table {
src/views/dashboard/index.vue
@@ -1,13 +1,22 @@
<script setup name="Dashboard">
import { ref } from "vue";
import PwButton from "@/components/pwButton.vue";
const acIdx = ref(0);
function changeIdx(idx) {
  acIdx.value = idx;
}
</script>
<template>
  <div class="page-contain bg-footer">
    首页
    <pw-button :active="acIdx === 0" text="品牌" @click="changeIdx(0)"></pw-button>
    <pw-button :active="acIdx === 1" @click="changeIdx(1)" style="margin-left: 4px;" text="电压等级"></pw-button>
  </div>
</template>
<style scoped lang="less">
.page-contain {
  padding: 10px 28px;
}
</style>
src/views/datas/addEdit.vue
@@ -19,9 +19,11 @@
    addDev,
    updateDev,
    addBatt,
    getMonCapByUid,
  } from "@/api/station";
  import powerTypes from '@/utils/const/const_powerType.js';
  import moment from 'moment';
  import useElement from "@/hooks/useElement.js";
  const { $loading, $message, $confirm } = useElement();
@@ -37,7 +39,7 @@
  const areaList = ref([]);
  const layout = {
    gutter: 16,
    span: 8
    span: 6
  };
  const addDevFlag = ref(0);
@@ -64,6 +66,8 @@
    product: "",
    battModel: "",
    powerType: 1,
    powerInuseTime: moment().format('YYYY-MM-DD'),
    inuseTime: moment().format('YYYY-MM-DD'),
  });
  const otherIdList = ref([]);
@@ -353,6 +357,19 @@
  const monVolList = ref([]);
  const productList = ref([]);
  const battModelList = ref([]);
  const monCapList = ref([]);
  // 获取标称容量
  function getMonCapList() {
    getMonCapByUid().then((res) => {
      let { code, data, data2 } = res;
      let list = [];
      if (code && data) {
        list = data2;
      }
      monCapList.value = list;
    });
  }
  // 获取电压等级
  function getVolLevels() {
@@ -442,6 +459,7 @@
    getDevTypeList();
    getMonVolList();
    getProductList();
    getMonCapList();
    if (info) {
      for(let key in info) {
@@ -483,7 +501,7 @@
<template>
  <div class="">
    <el-form ref="formRef" :model="form1" label-width="7em" :rules="rules">
    <el-form ref="formRef" :model="form1" label-width="8em" :rules="rules">
      <el-row :gutter="layout.gutter">
        <el-col :span="layout.span">
          <el-form-item label="省" prop="provice">
@@ -542,8 +560,6 @@
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="layout.gutter">
        <el-col :span="layout.span">
          <el-form-item label="机房名称" prop="stationName">
            <el-select
@@ -563,6 +579,8 @@
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="layout.gutter">
        <el-col :span="layout.span">
          <el-form-item label="电压等级" prop="stationType">
            <el-select
@@ -587,13 +605,13 @@
            <el-input v-model="form1.longitude" :disabled="info.addBattFlag"></el-input>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="layout.gutter">
        <el-col :span="layout.span">
          <el-form-item label="纬度" prop="latitude">
            <el-input v-model="form1.latitude" :disabled="info.addBattFlag"></el-input>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="layout.gutter">
        <el-col :span="layout.span">
          <el-form-item label="电源型号" prop="powerModel">
            <el-select
@@ -632,8 +650,6 @@
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="layout.gutter">
        <el-col :span="layout.span">
          <el-form-item label="电源协议" prop="protocol">
            <el-select
@@ -672,9 +688,26 @@
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="layout.gutter">
        <el-col :span="layout.span">
          <el-form-item label="电源IP" prop="powerIp">
            <el-input v-model="form1.powerIp" :disabled="info.addBattFlag"></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="layout.span">
          <el-form-item label="电源投运日期" prop="powerInuseTime">
            <el-date-picker
              v-model="form1.powerInuseTime"
              :disabled="info.addBattFlag"
              type="date"
              size="small"
              :clearable="false"
              :editable="false"
              placeholder="选择日期"
              format="YYYY-MM-DD"
              value-format="YYYY-MM-DD"
            />
          </el-form-item>
        </el-col>
      </el-row>
@@ -747,15 +780,15 @@
              <el-input v-model="form1.devIp" :disabled="info.addBattFlag && !addDevFlag"></el-input>
            </el-form-item>
          </el-col>
        </template>
      </el-row>
      <template v-if="addBinfFlag || info.addBattFlag">
        <el-row :gutter="layout.gutter">
          <el-col :span="layout.span">
            <el-form-item label="单体个数" prop="moncount">
              <el-input v-model="form1.moncount"></el-input>
            </el-form-item>
          </el-col>
        </template>
      </el-row>
      <template v-if="addBinfFlag || info.addBattFlag">
        <el-row :gutter="layout.gutter">
          <el-col :span="layout.span">
            <el-form-item label="标称电压" prop="monvolstd">
              <el-select
@@ -776,11 +809,22 @@
          </el-col>
          <el-col :span="layout.span">
            <el-form-item label="标称容量" prop="moncapstd">
              <el-input v-model="form1.moncapstd"></el-input>
              <el-select
                v-model="form1.moncapstd"
                filterable
                allow-create
                placeholder="请选择"
                style="width: 180px"
              >
                <el-option
                  v-for="(item, idx) in monCapList"
                  :key="'list11_' + idx"
                  :label="item"
                  :value="item"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="layout.gutter">
          <el-col :span="layout.span">
            <el-form-item label="标称内阻" prop="monresstd">
              <el-input v-model="form1.monresstd"></el-input>
@@ -804,11 +848,27 @@
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="layout.gutter">
          <el-col :span="layout.span">
            <el-form-item label="电池型号" prop="battModel">
              <el-input v-model="form1.battModel"></el-input>
            </el-form-item>
          </el-col>
          <el-col :span="layout.span">
            <el-form-item label="电池投运日期" prop="inuseTime">
              <el-date-picker
                v-model="form1.inuseTime"
                type="date"
                size="small"
                :clearable="false"
                :editable="false"
                placeholder="选择日期"
                format="YYYY-MM-DD"
                value-format="YYYY-MM-DD"
              />
            </el-form-item>
          </el-col>
        </el-row>
      </template>
      <div class="form-footer">
src/views/datas/device.vue
@@ -501,7 +501,7 @@
    <div class="page-footer"></div>
    <!-- 弹窗 -->
    <el-dialog :title="dialogTitle" v-model="addEditVisible" top="0" :close-on-click-modal="false" class="dialog-center"
      width="860px" center>
      width="1000px" center>
      <add-edit v-if="addEditVisible" @success="onOk" :info="datas.rowData" @cancel="onCanel"></add-edit>
    </el-dialog>
  </div>
src/views/dcPowerStatus/standardParams.vue
New file
@@ -0,0 +1,218 @@
<script setup name="StandardParams">
import {ref} from "vue";
import ycCard from "@/components/ycCard/index.vue";
import PwButton from "@/components/pwButton.vue";
import TabButton from "@/components/TabButton/index.vue";
import HdwButton from "@/components/HdwButton/index.vue";
import NoData from "@/components/noData.vue";
const headers = [
  {
    prop: "standName",
    label: "标准编号",
    width: "160",
  },
  {
    prop: "paramType",
    label: "参数类型",
    width: "160",
  },
  {
    prop: "basisVal",
    label: "基准值",
    width: "160",
  },
  {
    prop: "alarmLimith",
    label: "上限",
    width: "160",
  },
  {
    prop: "alarmLimithUpeper",
    label: "上上限",
    width: "160",
  },
  {
    prop: "alarmLimitl",
    label: "下限",
    width: "160",
  },
  {
    prop: "alarmLimitlLower",
    label: "下下限",
    width: "160",
  },
  {
    prop: "fileName",
    label: "规范文件名称",
    width: "160",
  },
];
const listRef = ref([]);
const acButton = ref(1);
function changeAcButton(num) {
  acButton.value = num;
}
const searchText = ref("");
const downloadText =ref("");
const fileList = ref([]);
</script>
<template>
  <div class="page-wrapper">
    <div class="page-content">
      <yc-card is-full>
        <div class="page-content-wrapper">
          <div class="page-content-tools page-filter">
            <div class="tools-left">
              <tab-button :active="acButton === 1" @click="changeAcButton(1)">直流电源</tab-button>
              <tab-button style="margin-left: 24px;" :active="acButton === 2" @click="changeAcButton(2)">通信电源</tab-button>
            </div>
            <div class="tools-right">
              <hdw-button>导出</hdw-button>
            </div>
          </div>
          <div class="page-content-table-wrapper">
            <div class="page-content-table">
              <div class="pos-rel">
                <div class="pos-abs">
                  <el-table class="yc-table" stripe height="100%" :data="listRef" style="width: 100%">
                    <el-table-column show-overflow-tooltip v-for="header in headers" :key="header.prop" :prop="header.prop" :label="header.label"
                                     :min-width="header.width" align="center">
                    </el-table-column>
                    <el-table-column label="操作" fixed="right" width="180" align="center">
                      <template #default="scope">
                        <el-button type="primary" size="small">参数设定</el-button>
                        <el-button type="success" size="small">上传</el-button>
                      </template>
                    </el-table-column>
                  </el-table>
                </div>
              </div>
            </div>
            <div class="file-list-wrapper">
              <div class="file-tools">
                <div class="file-tools-item">
                  <input class="file-tools-input" type="text" v-model="searchText" />
                  <hdw-button icon-class="search-data">查 询</hdw-button>
                </div>
                <div class="file-tools-item bottom">
                  <input disabled class="file-tools-input" type="text" v-model="downloadText" />
                  <hdw-button type="warning" icon-class="download">下 载</hdw-button>
                </div>
              </div>
              <div class="file-list-content">
                <div class="pos-rel">
                  <div class="pos-abs">
                    <el-scrollbar v-if="fileList.length !== 0"></el-scrollbar>
                    <no-data v-else text="暂无规范文件"></no-data>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </yc-card>
    </div>
  </div>
</template>
<style scoped lang="less">
.page-wrapper {
  display: flex;
  flex-direction: row;
  height: 100%;
  .page-content {
    flex: 1;
  }
}
.page-content-wrapper {
  display: flex;
  flex-direction: column;
  height: 100%;
  .page-content-tools {
    padding-bottom: 8px;
    margin-left: 26px;
    margin-right: 26px;
    display: flex;
    flex-direction: row;
    .tools-left {
      flex: 1;
    }
  }
  .page-content-table-wrapper {
    flex: 1;
    display: flex;
    flex-direction: row;
    .page-content-table {
      box-sizing: border-box;
      flex: 1;
      margin-left: 26px;
      margin-right: 8px;
    }
    .file-list-wrapper {
      min-width: 200px;
      padding: 16px;
      background-color: #073451;
      margin-right: 26px;
      display: flex;
      flex-direction: column;
      .file-list-content {
        flex:1;
      }
    }
  }
  .page-content-page {
    padding: 8px 8px 0 8px;
    text-align: center;
    .el-page-container {
      display: inline-block;
      padding: 0 16px;
    }
    .page-tool {
      display: inline-block;
    }
  }
}
.pos-rel {
  position: relative;
  width: 100%;
  height: 100%;
  .pos-abs {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    height: 100%;
  }
}
.file-tools {
  .file-tools-item {
    &.bottom {
      margin-top: 32px;
    }
    .file-tools-input {
      width: 240px;
      height: 38px;
      border: 0;
      background-color: #FFFFFF10;
      margin-right: 8px;
      color: #FFFFFF;
      font-size: 16px;
      &:hover {
        background-color: #076FE810;
      }
      &:focus {
        outline: 0;
      }
    }
  }
}
</style>
src/views/history/hisReal.vue
New file
@@ -0,0 +1,13 @@
<script setup name="hisReal">
import { ref } from "vue";
</script>
<template>
  <div class="">hisReal</div>
</template>
<style scoped lang="less">
</style>
src/views/history/hisTest.vue
New file
@@ -0,0 +1,65 @@
<script setup name="hisTest">
import { ref, reactive } from "vue";
const testTypes = [
  {
    prop: 'hrfd',
    label: '核容放电'
  },
  {
    prop: 'hrcd',
    label: '核容充电'
  },
  {
    prop: 'jkfd',
    label: '监控放电'
  },
  {
    prop: 'jkcd',
    label: '监控充电'
  },
  {
    prop: 'tdfd',
    label: '停电放电'
  }
];
const testRecordList = reactive({
  hrfd: [],
  hrcd: [],
  jkfd: [],
  jkcd: [],
  tdfd: []
});
</script>
<template>
  <div class="page-his">
    <div class="filter">
      <div class="item" v-for="(item, idx) in testTypes" :key="'list0_' + idx">
        <div class="label">{{ item.label }}</div>
        <div class="value">
          <el-select
            v-model="item.prop"
            size="small"
            clearable
            placeholder="请选择"
          >
            <el-option
              v-for="(item, idx) in testRecordList[item.prop]"
              :key="'list5_' + idx"
              :label="item.label"
              :value="item.value"
            >
            </el-option>
          </el-select>
        </div>
      </div>
    </div>
  </div>
</template>
<style scoped lang="less">
</style>
src/views/history/index.vue
New file
@@ -0,0 +1,214 @@
<script setup name="history">
import { ref, reactive, watch, onMounted } from "vue";
import siteList from "@/components/siteList/index.vue";
import  getQueryString  from "@/utils/getQueryString";
import formatSeconds from '@/utils/formatSeconds';
import hisTest from "./hisTest.vue";
import hisReal from "./hisReal.vue";
import { useRoute, useRouter } from "vue-router";
import useWebsocket from "@/hooks/useWebsocket";
const route = useRoute();
const router = useRouter();
const fullName = ref('湖北省-武汉市-武昌区-武昌机房-电池组1');
const pageTab = ref('his-test');
const show_num = ref(5);
</script>
<template>
  <div class="page-contain">
    <site-list @leaf-click="leafClick"></site-list>
    <div class="page-inner">
      <div class="page-header">
        <div class="btn-grp">
          <el-radio-group v-model="pageTab">
            <el-radio-button label="历史测试数据" value="his-test" />
            <el-radio-button label="历史实时数据" value="his-real" />
          </el-radio-group>
        </div>
        <div class="p-title">{{ fullName }}</div>
        <div class="btn-grp pos">
          <div class="data-granularity" v-if="pageTab === 'his-test'">
            <label style="font-size: 12px; margin-right: 8px">显示粒度:</label>
              <el-select
                v-model="show_num"
                size="small"
                style="width: 90px"
                @change="testRecordChange"
              >
                <el-option :value="1" label="×1"></el-option>
                <el-option :value="2" label="×2"></el-option>
                <el-option :value="3" label="×3"></el-option>
                <el-option :value="4" label="×4"></el-option>
                <el-option :value="5" label="×5"></el-option>
                <el-option :value="6" label="×6"></el-option>
                <el-option :value="7" label="×7"></el-option>
                <el-option :value="8" label="×8"></el-option>
                <el-option :value="9" label="×9"></el-option>
                <el-option :value="10" label="×10"></el-option>
              </el-select>
          </div>
          <el-button type="primary" size="small">实时监测</el-button>
        </div>
      </div>
      <div class="page-main">
        <!-- 历史测试数据 -->
        <his-test v-if="pageTab == 'his-test'"></his-test>
        <!-- 历史实时数据 -->
        <his-real v-if="pageTab == 'his-real'"></his-real>
      </div>
    </div>
  </div>
</template>
<style scoped lang="less">
.page-contain {
  display: flex;
  padding: 8px 8px 8px 0;
  margin-left: 8px;
  overflow: hidden;
  .page-inner {
    display: flex;
    flex-direction: column;
    flex: 1;
    margin-left: 8px;
  }
  .page-header {
    // border: 1px solid #0ff;
    background: #071426;
    position: relative;
    display: flex;
    align-items: center;
    border-bottom: 1px solid #497AAB;
    padding-right: 16em;
    .btn-grp {
      display: flex;
      align-items: center;
      .data-granularity {
        margin-right: 0.6em;
      }
      &.pos {
        position: absolute;
        right: 8px;
        top: 0;
        height: 100%;
      }
      :deep(.el-radio-group) {
        label {
          ~ label {
            margin-left: 10px;
          }
        }
        span {
          border-radius: 0;
          border: 0 none;
          background: #134bab;
          color: #fff;
        }
        .is-active {
          span {
            background-color: #47cafe;
            color: #0e344e;
          }
        }
      }
    }
    .p-title {
      flex: 1;
      font-size: 18px;
      font-weight: 700;
      color: #50c7f1;
      height: 42px;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    .status-bar {
      height: 72px;
      display: flex;
      flex-direction: row;
      .status-item {
        flex: 1;
        font-size: 14px;
        font-weight: 700;
        color: #50c7f1;
        // border: 1px solid #0F6B79;
        border-radius: 6px;
        margin: 4px 2px;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        box-shadow: inset 0 0 30px -10px #0F6B79;
        background: radial-gradient(circle at -10% center, #1F6571 5%, transparent 20%) no-repeat,
            radial-gradient(circle at 110% center, #1F6571 5%, transparent 20%) no-repeat,
            radial-gradient(circle at center -72%, #1F6571 12%, transparent 50%) no-repeat,
            radial-gradient(circle at center 138%, #1F6571 12%, transparent 50%) no-repeat;
        .item-value {
          margin-bottom: 6px;
          color: #ff0;
          font-size: 20px;
          &.time {
            white-space: nowrap;
            font-size: 12px;
          }
        }
        .item-name {
          color: #6DCCE6;
        }
      }
    }
    .p-tabs {
      display: flex;
      flex-direction: row;
      height: 46px;
      border: 1px solid #426EA0;
      padding: 4px;
      .tab-item {
        font-size: 14px;
        font-weight: 700;
        flex: 1;
        display: flex;
        justify-content: center;
        align-items: center;
        margin-left: 2px;
        margin-right: 2px;
        background: #073451;
        cursor: pointer;
        &.active {
          background: #47CAFE;
          color: #03216e;
        }
        &.btn-item {
          background: #076fe8;
          // border-radius: 6px;
        }
      }
    }
  }
  .page-main {
    flex: 1;
    padding-left: 0;
    padding-right: 0;
    .tab-contain {
      height: 100%;
    }
  }
}
</style>
src/views/login/index.vue
@@ -1,7 +1,7 @@
<template>
  <div class="login-container">
    <div class="site-title">
      <svg-icon icon-class="lock1"></svg-icon>{{ platformName }}
      <div class="logo"><img :src="logUrl" alt=""></div>{{ platformName }}
    </div>
    <div class="img-hm"></div>
    <div class="login-wrap">
@@ -61,8 +61,11 @@
          </el-form-item>
        </el-tooltip>
        <el-form-item>
          <el-row :gutter="8">
              <el-col :span="14">
          <el-row class="el_row" :gutter="0">
              <span class="svg-container pos">
                <svg-icon icon-class="dun" />
              </span>
              <el-col :span="16">
                <el-input
                  class="input"
                  placeholder="验证码"
@@ -70,7 +73,7 @@
                  @keyup.enter="handleLogin"
                ></el-input>
              </el-col>
              <el-col :span="10">
              <el-col :span="8">
                <v-sidentify
                  :identifyCode="verifyCode"
                  @click="changeVerifyCode"
@@ -81,7 +84,7 @@
        <el-button
          :loading="loading"
          type="primary"
          style="width:100%; height:50px; margin-bottom:40px;"
          style="width:100%; height:50px; background: #40CFF7; color: #070E28; font-size:20px; margin-bottom:40px;"
          @click.prevent="handleLogin"
          >登录</el-button
        >
@@ -106,6 +109,8 @@
import defaultSettings from '@/settings';
import VSidentify from './components/verifyComponent.vue';
import gjdw from '@/assets/images/gjdw-log.png';
const platformName = defaultSettings.title;
const route = useRoute();
@@ -120,6 +125,8 @@
const verifyCode = ref('');
const verify = ref('');
const logUrl = ref(gjdw);
const validateUsername = (_rule, value, callback) => {
@@ -296,11 +303,11 @@
/* 修复input 背景不协调 和光标变色 */
/* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */
$bg: #283443;
$light_gray: #fff;
// $bg: #283443;
// $light_gray: #fff;
$cursor: #999;
$input_bg: rgba(0, 0, 0, 0.1);
$input_color: #000;
// $input_bg: rgba(0, 0, 0, 0.1);
// $input_color: #000;
@supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
  .login-container .el-input input {
@@ -325,21 +332,21 @@
    input {
      padding: 12px 5px 12px 15px;
      color: $input_color;
      color: #fff;
      height: 47px;
      caret-color: $cursor;
      &:-webkit-autofill {
        box-shadow: 0 0 0px 1000px $input_bg inset !important;
        -webkit-text-fill-color: $input_color !important;
        box-shadow: 0 0 0px 1000px rgba(255, 255, 255, 0.1) inset !important;
        -webkit-text-fill-color: #fff !important;
      }
    }
  }
  .el-form-item {
    border: 1px solid rgba(255, 255, 255, 0.1);
    border: 1px solid #247CF7;
    background: rgba(0, 0, 0, 0.1);
    border-radius: 5px;
    // border-radius: 5px;
    color: #454545;
  }
}
@@ -351,45 +358,95 @@
$light_gray: #eee;
.login-container {
  min-height: 100%;
  height: 100%;
  width: 100%;
  background-image: url("@/assets/images/login-bg.jpg");
  background: url("@/assets/images/bg-login1.png"),
      url("@/assets/images/bg-login0.jpg");
  background-size: 100% 100%;
  background-repeat: no-repeat;
  overflow: hidden;
  // &::before {
  //   content: "";
  //   position: absolute;
  //   top: 0;
  //   left: 0;
  //   width: 100%;
  //   height: 100%;
  //   background: url("@/assets/images/bg-login1.png");
  // }
  .login-wrap {
    // display: none;
    position: relative;
    width: 520px;
    max-width: 100%;
    // width: 80vh;
    width: 450px;
    height: 47vh;
    left: 50%;
    top: 50%;
    transform: translate(-51%, -58%);
    // max-width: 100%;
    // margin: 260px 10% 0 auto;
    margin: 260px auto 0;
    transform: translate(clamp(0px, calc(36vw - 50%), 50vw), 0);
    // margin: 260px auto 0;
    // transform: translate(clamp(0px, calc(36vw - 50%), 50vw), 0);
    padding: 8px;
    overflow: hidden;
    // background-color: #1d4167;
    background: rgba(255, 255, 255, 0.2);
    border: 2px solid #0d3c68;
    // background: rgba(255, .255, 255, 0.2);
    // border: 2px solid #0d3c68;
    display: flex;
    justify-content: center;
    align-items: center;
    .login-form {
      background: #fff;
      padding: 36px 70px 36px;
      border: 2px solid #064ec3;
      // display: none;
      // background: #fff;
      // width: 46vh;
      width: 100%;
      // padding: 36px 70px 36px;
      // border: 2px solid #064ec3;
    }
    .input-item {
      margin-bottom: 28px;
    }
  }
  .el_row {
    width: 100%;
  }
  .site-title {
    font-size: 30px;
    font-size: 46px;
    font-weight: 700;
    color: #07fcff;
    color: #0ff;
    position: absolute;
    left: 0;
    top: 12%;
    transform: translate(clamp(0px, calc(18vw - 50%), 50vw), -50%);
    transform: translate(clamp(0px, calc(24vw - 50%), 50vw), -50%);
    display: flex;
    align-items: center;
    .logo {
      position: relative;
      width: 5.5rem;
      height: 2.5rem;
      margin-right: 24px;
      img {
        width: 100%;
        height: 100%;
        object-fit: contain;
        // mask-image: linear-gradient(#fff, #fff);
        // mask-size: cover;
        // mask-repeat: no-repeat;
      }
      &::after {
        position: absolute;
        content: '';
        top: 50%;
        right: -12px;
        width: 2px;
        height: 80%;
        transform: translateY(-50%);
        background: #0ff;
      }
    }
  }
  .tips {
@@ -416,21 +473,35 @@
  .svg-container {
    padding: 6px 5px 6px 15px;
    color: $dark_gray;
    color: #fff;
    vertical-align: middle;
    width: 30px;
    display: inline-block;
    &.pos {
      position: absolute;
      left: 0;
      top: 0;
      font-size: 20px;
      padding-left: 10px;
      & + :deep(.el-col .el-input__wrapper) {
        padding-left: 41px;
      }
    }
  }
  .title-container {
    position: relative;
    .title {
      font-size: 18px;
      color: #0296f3;
      margin: 0px auto 40px auto;
      // text-align: center;
      font-weight: bold;
      font-size: 38px;
      color: #fff;
      // margin: 0px auto 40px auto;
      margin-bottom: 30px;
      margin-top: 0;
      text-align: center;
      letter-spacing: 0.2em;
      // font-weight: bold;
      font-weight: normal;
    }
  }
@@ -439,7 +510,7 @@
    right: 10px;
    top: 7px;
    font-size: 16px;
    color: $dark_gray;
    color: #fff;
    cursor: pointer;
    user-select: none;
  }
src/views/realtime/index.vue
@@ -7,6 +7,7 @@
import  getQueryString  from "@/utils/getQueryString";
import formatSeconds from '@/utils/formatSeconds';
import { useRoute, useRouter } from "vue-router";
import useWebsocket from "@/hooks/useWebsocket";
@@ -195,7 +196,7 @@
      </div>
      <div class="page-main">
        <div class="tab-contain" v-if="acTab == 'system'">
          <tab-system :data="rtData['system']"></tab-system>
          <tab-system :data="rtData['system']" :powerId="curPowerId" :devId="curDevId" :battgroupId="curBattgroupId"></tab-system>
        </div>
        <div class="tab-contain" v-if="acTab == 'power'">
          <tab-power></tab-power>
@@ -318,7 +319,8 @@
  .page-main {
    flex: 1;
    padding-left: 0;
    padding-right: 0;
    .tab-contain {
      height: 100%;
    }
src/views/realtime/tabs/system.vue
@@ -1,22 +1,131 @@
<script setup>
import { ref } from "vue";
import { nextTick, ref, watch, onMounted } from "vue";
import svgDiagram from '@/components/svgDiagram.vue';
import info from '@/components/info.vue';
// import lineChart from '@/components/echarts/line1.vue';
import lineChart from '@/components/echarts/BaseChart.vue';
import lineChart from '@/components/echarts/line1.vue';
import lineChart2 from '@/components/echarts/line2.vue';
// import lineChart from '@/components/echarts/BaseChart.vue';
// import lineChart from '../bar8.vue';
import {
    getHalfHourBattDevData,
    getHalfHourPwrHisAcinData,
    getHalfHourPwrHisDcoutData,
} from "@/api/realtime";
const props = defineProps({
  data: {
    type: Object,
    default: {},
  },
    data: {
        type: Object,
        default: {},
    },
    powerId: {
        type: [Number, String],
    },
    devId: {
        type: [Number, String],
    },
    battgroupId: {
        type: [Number, String],
    },
});
const tabIdx0 = ref(0);
const tabIdx1 = ref(0);
const chart0 = ref(null);
const chart1 = ref(null);
const chart2 = ref(null);
watch(
    () => tabIdx0.value,
    () => {
        nextTick(() => {
            tabIdx0Change();
        });
    },
    { immediate: true }
);
watch(
    () => tabIdx1.value,
    () => {
        nextTick(() => {
            tabIdx1Change();
        });
    },
    { immediate: true }
);
watch(
    () => props.powerId,
    (n) => {
    if (!n) return;
        nextTick(() => {
            getAcinData();
            getDevData();
            getDcoutData();
        });
    },
    { immediate: true }
)
function tabIdx0Change() {
    // TODO
    if (chart0.value) {
        chart0.value.updateChart([1, 2, 3], [100, 200, 300]);
    }
}
function tabIdx1Change() {
    // TODO
    if (chart1.value) {
        chart1.value.updateChart([1, 2, 3], [100, 200, 300]);
    }
}
async function getAcinData(granularity = 5) {
    let { code, data, data2 } = await getHalfHourPwrHisAcinData(props.powerId, granularity);
    let list = [];
    if (code && data) {
        list = data2;
    }
    return list;
}
async function getDevData(granularity = 5) {
    let { code, data, data2 } = await getHalfHourBattDevData(props.battgroupId, granularity);
    let list = [];
    if (code && data) {
        list = data2;
    }
    return list;
}
async function getDcoutData(granularity = 5) {
    let { code, data, data2 } = await getHalfHourPwrHisDcoutData(props.powerId, granularity);
    let list = [];
    if (code && data) {
        list = data2;
    }
    return list;
}
onMounted(async () => {
    // await getAcinData();
    // await getDevData();
    // await getDcoutData();
    if (chart2.value) {
        chart2.value.updateChart(['04:12', '04:13', '04:14'], {
            '设备温度': [100, 200, 220],
            '组端电流': [100, 200, 220],
            '组端电压': [100, 200, 220],
            '负载电流': [100, 200, 220],
        });
    }
});
</script>
<template>
@@ -68,12 +177,16 @@
        </div>
        <div class="p-right">
          <div class="control-contain">
            <div class="control-title"><svg-icon icon-class="controls"></svg-icon><span>控制管理</span></div>
            <div class="control-title">
              <svg-icon icon-class="controls"></svg-icon><span>控制管理</span>
            </div>
            <div class="control-btn">核容测试</div>
            <div class="control-btn">停止核容测试</div>
          </div>
          <div class="control-contain">
            <div class="control-title"><svg-icon icon-class="dev"></svg-icon><span>设备管理</span></div>
            <div class="control-title">
              <svg-icon icon-class="dev"></svg-icon><span>设备管理</span>
            </div>
            <div class="control-btn">远程重启</div>
            <div class="control-btn">系统参数设置</div>
            <div class="control-btn">告警参数设置</div>
@@ -94,7 +207,10 @@
            </el-radio-group>
            <svg-icon class-name="btn-setting" icon-class="setting"></svg-icon>
          </template>
          <line-chart></line-chart>
          <line-chart
            ref="chart0"
            :type="['电流', '电压'][tabIdx0]"
          ></line-chart>
        </card>
      </div>
      <div class="card-item">
@@ -106,6 +222,10 @@
            </el-radio-group>
            <svg-icon class-name="btn-setting" icon-class="setting"></svg-icon>
          </template>
          <line-chart
            ref="chart1"
            :type="['电流', '电压'][tabIdx1]"
          ></line-chart>
        </card>
      </div>
      <div class="card-item">
@@ -113,43 +233,23 @@
          <template #tools>
            <svg-icon class-name="btn-setting" icon-class="setting"></svg-icon>
          </template>
          <line-chart2
            ref="chart2"
            :type="['电流', '电压'][tabIdx1]"
          ></line-chart2>
        </card>
      </div>
      <div class="card-item">
        <card title="蓄电池信息">
          <div class="batt grid">
            <info
              label="最大容量"
              value="#3 8Ah"
            ></info>
            <info
              label="最小容量"
              value="100"
            ></info>
            <info
              label="最高内阻"
              value="100"
            ></info>
            <info
              label="最低内阻"
              value="100"
            ></info>
            <info
              label="最高电压"
              value="100"
            ></info>
            <info
              label="最低电压"
              value="100"
            ></info>
            <info
              label="最高温度"
              value="100"
            ></info>
            <info
              label="最低温度"
              value="100"
            ></info>
            <info label="最大容量" value="#3 8Ah"></info>
            <info label="最小容量" value="100"></info>
            <info label="最高内阻" value="100"></info>
            <info label="最低内阻" value="100"></info>
            <info label="最高电压" value="100"></info>
            <info label="最低电压" value="100"></info>
            <info label="最高温度" value="100"></info>
            <info label="最低温度" value="100"></info>
          </div>
        </card>
      </div>
@@ -164,24 +264,30 @@
  padding-right: 8px;
  display: flex;
  flex-direction: column;
  .row {
    flex: 1.52;
    .row-content {
      display: flex;
      flex-direction: row;
      height: 100%;
    }
    .p-left {
      width: 240px;
      display: flex;
      flex-direction: column;
      .p-item {
        margin: 8px;
        display: flex;
        flex-direction: column;
        .panel-title {
          font-size: 24px;
        }
        .panel {
          border: 1px solid #5FA9CF;
          background: #073451;
@@ -192,18 +298,22 @@
          font-size: 12px;
          display: flex;
          flex-direction: column;
          .panel-row {
            display: flex;
            flex-direction: row;
            margin-bottom: 4px;
            .label {
              flex: 0 0 6em;
              margin-right: 0.6em;
              text-align: right;
              &::after {
                content: ":";
              }
            }
            .value {
              flex: 1;
            }
@@ -211,9 +321,11 @@
        }
      }
    }
    .p-main {
      flex: 1;
    }
    .p-right {
      width: 160px;
      margin-left: 20px;
@@ -221,6 +333,7 @@
      padding-top: 20px;
      display: flex;
      flex-direction: column;
      .control-contain {
        display: flex;
        flex-direction: column;
@@ -231,6 +344,7 @@
        padding-bottom: 4px;
        overflow: hidden;
        font-size: 12px;
        .control-title {
          background: #0B415D;
          font-size: 20px;
@@ -238,10 +352,12 @@
          text-align: center;
          font-weight: 700;
          margin-bottom: 4px;
          span {
            margin-left: 0.6em;
          }
        }
        .control-btn {
          margin: 2px 4px;
          cursor: pointer;
@@ -250,14 +366,17 @@
          padding: 6px 0;
          text-align: center;
          border-radius: 4px;
          &:hover {
            color: #FDFE01;
            border: 1px solid #DF7B26;
            box-shadow: inset 0 0 10px 4px #DF7B26;
          }
        }
        &.no-wrap {
          background: transparent;
          .control-btn {
            flex: 1;
            border: 1px solid #0BF9FE;
@@ -265,6 +384,7 @@
            font-weight: bold;
            font-size: 14px;
            box-shadow: inset 0 0 10px 4px #0BF9FE;
            &:hover {
              color: #FDFE01;
              border: 1px solid #DF7B26;
@@ -274,26 +394,28 @@
        }
      }
    }
    &.row2 {
      flex: 1;
      margin-top: 4px;
      display: flex;
      flex-direction: row;
      .card-item {
        flex: 1;
        margin: 4px;
      }
    }
  }
  .svg-diagram {
  }
  .svg-diagram {}
  .btn-setting {
    display: inline-block;
    cursor: pointer;
    margin-left: 0.4em;
    transition: all 1.3s ease;
    &:hover {
      transform: rotate(360deg);
      color: #FDFE01;
@@ -317,26 +439,28 @@
      border-radius: 0;
      box-shadow: none !important;
    }
    // .el-radio-button--small .el-radio-button__inner {
    //     border-radius: 0;
    //     font-size: 12px;
    //     padding: 5px 11px;
    // }
    :deep(.el-radio-button__inner) {
        background: #183A55;
        border: 1px solid #4D81BA;
        border-left: 0;
        border-radius: 0;
        color: #fff;
        font-weight: 500;
        padding: 4px 10px;
      background: #183A55;
      border: 1px solid #4D81BA;
      border-left: 0;
      border-radius: 0;
      color: #fff;
      font-weight: 500;
      padding: 4px 10px;
    }
    :deep(.el-radio-button.is-active .el-radio-button__original-radio:not(:disabled)+.el-radio-button__inner) {
        background: linear-gradient(69deg, #6EABE4, #6EABE4 25%,  #0A3E77 75%, #0A3E77);
        // border: 0 none;
        box-shadow: none;
        color: #fff;
      background: linear-gradient(69deg, #6EABE4, #6EABE4 25%, #0A3E77 75%, #0A3E77);
      // border: 0 none;
      box-shadow: none;
      color: #fff;
    }
  }
}
</style>
</style>
src/views/statistics/batt.vue
@@ -1,5 +1,5 @@
<script setup name="batt">
    import { ref, reactive, onMounted, computed } from "vue";
    import { ref, reactive, onMounted, computed, nextTick } from "vue";
    import { storeToRefs } from "pinia";
    import { Search, Plus } from "@element-plus/icons-vue";
    import ycCard from "@/components/ycCard/index.vue";
@@ -13,139 +13,99 @@
    proviceList, cityList, countryList, stationList,
  } = useStation();
  import { useRouter } from "vue-router";
  const router = useRouter();
  import { ExportFile } from '@/utils/exportFile.js';
  import powerTypes from '@/utils/const/const_powerType.js';
  import hrTypes from '@/utils/const/const_hrTestType.js';
  import {
    delBatt,
  } from "@/api/station";
  const userStore = useUserStore();
  const { uid, uname } = storeToRefs(userStore);
  import moment from 'moment';
  import formatSeconds from '@/utils/formatSeconds';
  import {
    toFixed,
    digits,
  } from '@/utils/toFixed';
  import {
    getDevList,
  } from "@/api/station";
    getBattStatistic,
  } from "@/api/statistic.js";
    const { $loading, $message, $confirm } = useElement();
    const headers = [
    {
            prop: "provice",
            label: "省",
            width: "80",
            width: "100",
        },
    {
            prop: "city",
            label: "市",
            width: "80",
            width: "100",
        },
    {
            prop: "country",
            label: "区县",
            width: "80",
            width: "100",
        },
        {
            prop: "stationName",
            label: "机房名称",
    {
            prop: "fullName",
            label: "站点名称",
            width: "160",
        },
    {
            prop: "stationType",
            label: "电压等级",
            width: "80",
        },
    {
            prop: "longitude",
            label: "经度",
            width: "80",
        },
    {
            prop: "latitude",
            label: "纬度",
            width: "80",
        },
    {
            prop: "powerName",
            label: "电源名称",
            width: "80",
        },
    {
            prop: "powerTypeStr",
            label: "电源类型",
            width: "80",
        },
    {
            prop: "company",
            label: "电源品牌",
            width: "80",
        },
        {
            prop: "powerModel",
            label: "电源型号",
            width: "80",
        },
    {
            prop: "protocol",
            label: "电源协议",
            width: "80",
        },
    {
            prop: "powerIp",
            label: "电源IP",
            width: "120",
        },
    {
            prop: "devName",
      prop: "devName",
            label: "设备名称",
            width: "120",
            width: "80",
        },
    {
            prop: "devType",
            label: "设备型号",
            width: "120",
        },
    {
            prop: "devIp",
            label: "设备IP",
            width: "120",
        },
    {
            prop: "battgroupName",
      prop: "battgroupName",
            label: "电池组名称",
            width: "120",
            width: "80",
        },
    {
            prop: "moncount",
            label: "电池单体个数",
            width: "120",
        },
      prop: "moncount",
      label: "单体数目",
      width: "160",
    },
    {
            prop: "monvolstd",
            label: "电池标称电压",
            width: "120",
            label: "标称电压",
            width: "80",
        },
    {
            prop: "moncapstd",
            label: "电池标称容量",
            width: "120",
            label: "标称容量",
            width: "80",
        },
    {
            prop: "monresstd",
            label: "电池标称内阻",
            width: "120",
        },
    {
            prop: "product",
            label: "电池品牌",
            width: "120",
        },
    {
            prop: "battModel",
            label: "电池型号",
            width: "120",
        },
      prop: "monresstd",
      label: "标称内阻",
      width: "80",
    },
    ];
   const testType = ref('');
  const hrTypeList = computed(() => {
    return Object.keys(hrTypes).map(v => {
      return {
        value: v,
        label: hrTypes[v],
      };
    });
  });
    const background = ref(true);
    const disabled = ref(false);
    const pageCurr = ref(1);
@@ -155,30 +115,32 @@
    const dialogTitle = ref("");
    const currentAreaId = ref();
    const currentAreaIds = ref([]);
  const testStartDate = ref('');
  const testEndDate = ref('');
    const datas = reactive({
        tableData: [],
        rowData: {},
    });
    // const tableData = reactive([]);
    // const rowData = reactive({});
    // const userStore = useUserStore();
    // const { uid, uname } = storeToRefs(userStore);
  // 计算属性生成 picker-options(更简洁)
  const startDisabledDate = (time) =>  testEndDate.value ? moment(testEndDate.value).isBefore(time) || moment().isBefore(time) : moment().isBefore(time);
  const endDisabledDate = (time) => testStartDate.value ? moment(time).isBefore(testStartDate.value) || moment().isBefore(time) : moment().isBefore(time);
    function getList() {
        let loading = $loading();
    let params = {
      // city: "",
      // country: "",
      provice: provice.value || undefined,
      city: city.value || undefined,
      country: country.value || undefined,
      stationName: stationName.value || undefined,
      // testType: testType.value || undefined,
      pageNum: pageCurr.value,
      pageSize: pageSize.value,
      // powerName: "",
      // provice: "",
      // stationName: "",
    };
        getDevList(params)
        getBattStatistic(params)
            .then((res) => {
                let { code, data, data2 } = res;
                let list = [];
@@ -187,8 +149,6 @@
                    // console.log(data);
                    list = data2.list.map(v => ({
            ...v,
            powerTypeStr: powerTypes[v.powerType],
            // roleName: roles[v.role],
          }));
                    _total = data2.total;
                }
@@ -214,61 +174,44 @@
        pageCurr.value = val;
        getList();
    }
    function add() {
        dialogTitle.value = "添加设备";
        datas.rowData = {};
        addEditVisible.value = true;
    }
    function edit(record) {
        dialogTitle.value = "编辑设备";
        addEditVisible.value = true;
        console.log(record, '=======edit======');
        // if (record.ainfList.idPath) {
        //     ids = record.ainfList.idPath.split("_").map((v) => v * 1);
        // }
        // ids.push(record.areaId);
        datas.rowData = { ...record };
    }
  function addBatt(record) {
    dialogTitle.value = "添加电池组";
        datas.rowData = { ...record, addBattFlag: true };
        addEditVisible.value = true;
  function filterChange() {
    nextTick(() => {
      pageCurr.value = 1;
      getList();
    });
  }
    function confirmRemove(record) {
        $confirm("删除", () => {
      let { stationId, powerId, battgroupId } = record;
            remove(stationId, powerId, battgroupId||undefined);
        });
    }
    function remove(stationId, powerId, battgroupId) {
        let loading = $loading();
        delBatt(stationId, powerId, battgroupId)
            .then((res) => {
                let { code, data } = res;
                loading.close();
                if (code && data) {
                    $message.success("操作成功");
                    handleCurrentChange(1);
                } else {
                    $message.success("操作失败");
                }
            })
            .catch((err) => {
                loading.close();
                console.log(err);
            });
    }
    function onOk() {
        addEditVisible.value = false;
        handleCurrentChange(1);
    }
    function onCanel() {
        addEditVisible.value = false;
    }
  function goRt (row) {
    router.push({
      path: '/datas/realtime',
      query: {
        stationId: row.stationId || undefined,
        powerId: row.powerId || undefined,
        devId: row.devId || undefined,
        battgroupId: row.battgroupId || undefined,
        pageFlag: Math.random(),
      }
    });
  }
  function goHis (row) {
    router.push({
      path: '/datas/history',
      query: {
        stationId: row.stationId || undefined,
        powerId: row.powerId || undefined,
        devId: row.devId || undefined,
        battgroupId: row.battgroupId || undefined,
        pageTab: 'his-real',
        pageFlag: Math.random(),
      }
    });
  }
  function exportExcel() {
    ExportFile(headers, datas.tableData, "电池组信息统计");
  }
    onMounted(() => {
        getList();
@@ -283,102 +226,86 @@
      <yc-card is-full>
        <div class="page-content-wrapper">
          <div class="page-content-tools page-filter">
            <div class="table-row">
              <div class="table-cell text-right">省:</div>
              <div class="table-cell">
                <el-select
                  v-model="provice"
                  size="small"
                  placeholder="请选择省"
                >
                  <el-option
                    v-for="item in proviceList"
                    :key="'l0_' + item"
                    :label="item"
                    :value="item"
            <div class="grid-container" :style="{'--counter': 4}">
              <div class="grid-item">
                <div class="label">省</div>
                <div class="value">
                  <el-select
                    v-model="provice"
                    size="small"
                    clearable
                    @change="filterChange"
                    placeholder="请选择省"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in proviceList"
                      :key="'l0_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="table-cell text-right">市:</div>
              <div class="table-cell">
                <el-select
                  v-model="city"
                  size="small"
                  placeholder="请选择市"
                >
                  <el-option
                    v-for="item in cityList"
                    :key="'l1_' + item"
                    :label="item"
                    :value="item"
              <div class="grid-item">
                <div class="label">市</div>
                <div class="value">
                  <el-select
                    v-model="city"
                    size="small"
                    clearable
                    @change="filterChange"
                    placeholder="请选择市"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in cityList"
                      :key="'l1_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="table-cell text-right">区县:</div>
              <div class="table-cell">
                <el-select
                  v-model="country"
                  size="small"
                  placeholder="请选择区县"
                >
                  <el-option
                    v-for="item in countryList"
                    :key="'l2_' + item"
                    :label="item"
                    :value="item"
              <div class="grid-item">
                <div class="label">区县</div>
                <div class="value">
                  <el-select
                    v-model="country"
                    clearable
                    @change="filterChange"
                    size="small"
                    placeholder="请选择区县"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in countryList"
                      :key="'l2_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="table-cell text-right">站点:</div>
              <div class="table-cell">
                <el-select
                  v-model="stationName"
                  size="small"
                  placeholder="请选择站点"
                >
                  <el-option
                    v-for="item in stationList"
                    :key="'l3_' + item.stationId"
                    :label="item.stationName"
                    :value="item.stationName"
              <div class="grid-item">
                <div class="label">站点</div>
                <div class="value">
                  <el-select
                    v-model="stationName"
                    clearable
                    @change="filterChange"
                    size="small"
                    placeholder="请选择站点"
                  >
                  </el-option>
                </el-select>
              </div>
              <div class="table-cell text-right">品牌:</div>
              <div class="table-cell">
                <el-select
                  v-model="country"
                  size="small"
                  placeholder="请选择品牌"
                >
                  <el-option
                    v-for="item in countryList"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                  >
                  </el-option>
                </el-select>
              </div>
              <div class="table-cell text-right">电压等级:</div>
              <div class="table-cell">
                <el-select
                  v-model="country"
                  size="small"
                  placeholder="请选择电压等级"
                >
                  <el-option
                    v-for="item in countryList"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in stationList"
                      :key="'l3_' + item.stationId"
                      :label="item.stationName"
                      :value="item.stationName"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
            </div>
          </div>
@@ -389,16 +316,12 @@
                  <el-table-column type="index" fixed="left" label="序号" width="60"></el-table-column>
                  <el-table-column v-for="header in headers" :key="header.prop" :prop="header.prop" :label="header.label"
                    :min-width="header.width" align="center">
                    <template #default="scope">{{ scope.row[header.prop] || '--' }}</template>
                    <template #default="scope">{{ scope.row[header.prop] }}</template>
                  </el-table-column>
                  <el-table-column label="操作" fixed="right" width="240" align="center">
                    <template #default="scope">
                      <el-button type="primary" size="small"
                        @click="edit(scope.row)">编辑</el-button>
                      <el-button type="danger" size="small"
                        @click="confirmRemove(scope.row)">删除</el-button>
                      <el-button type="primary" size="small"
                        @click="addBatt(scope.row)">添加电池组</el-button>
                      <el-button type="warning" size="small" @click="goRt(scope.row)">实时监测</el-button>
                      <el-button type="primary" size="small" @click="goHis(scope.row)">历史数据</el-button>
                    </template>
                  </el-table-column>
                </el-table>
@@ -407,7 +330,6 @@
          </div>
          <div class="page-content-page">
            <div class="page-tool">
              <el-button type="primary" round size="small" @click="add" :icon="Plus">添加</el-button>
              <el-button type="primary" round size="small" @click="getList" :icon="Search">查询</el-button>
            </div>
            <div class="el-page-container">
@@ -416,17 +338,14 @@
                :background="background" layout="total, sizes, prev, pager, next, jumper" :total="total"
                @size-change="handleSizeChange" @current-change="handleCurrentChange" />
            </div>
            <div class="page-tool"></div>
            <div class="page-tool">
              <el-button type="primary" round size="small" @click="exportExcel">导出</el-button>
            </div>
          </div>
        </div>
      </yc-card>
    </div>
    <div class="page-footer"></div>
    <!-- 弹窗 -->
    <el-dialog :title="dialogTitle" v-model="addEditVisible" top="0" :close-on-click-modal="false" class="dialog-center"
      width="860px" center>
      <add-edit v-if="addEditVisible" @success="onOk" :info="datas.rowData" @cancel="onCanel"></add-edit>
    </el-dialog>
  </div>
</template>
@@ -513,4 +432,7 @@
    }
  }
}
.max-width {
  max-width: 200px;
}
</style>
src/views/statistics/battCell.vue
New file
@@ -0,0 +1,554 @@
<script setup name="battCell">
import { ref, reactive, onMounted, computed, nextTick } from "vue";
import useElement from "@/hooks/useElement.js";
import barChart from '@/components/echarts/bar1.vue';
import lineChart from '@/components/echarts/line3.vue';
import * as echarts from 'echarts';
import moment from 'moment';
  import {
    getSingleStatistic,
    battMonExport,
  } from "@/api/statistic.js";
  import {
    getBatteryBrand,
    getMonCapByUid,
    getMonVol,
  } from "@/api/station";
  const { $loading, $message, $confirm } = useElement();
  const barLabels = ['优秀', '劣化', '损坏'];
  const chart0 = ref(null);
const chart1 = ref(null);
const chart2 = ref(null);
const allGraph = ref(null);
let allGraphInstance = null;
const goodlistNum = ref(0);
const badlistNum = ref(0);
const damagelistNum = ref(0);
const goodlist = ref([]);
const badlist = ref([]);
const damagelist = ref([]);
const chartData = reactive({
  chart0: {
    labels: [],
    volDatas: [],
    resDatas: [],
  },
  chart1: {
    labels: [],
    volDatas: [],
    resDatas: [],
  },
  chart2: {
    labels: [],
    volDatas: [],
    resDatas: [],
  },
});
const startDate0 = '2025-01-01 00:00:00';
const startDate = ref(moment().subtract(30, 'day').format('YYYY-MM-DD'));
const endDate = ref(moment().format('YYYY-MM-DD'));
const moncapstd = ref('');
const monvolstd = ref('');
const product = ref('');
const startDisabledDate = (time) =>  endDate.value ? moment(endDate.value).isBefore(time) || moment().isBefore(time) : moment().isBefore(time);
  const endDisabledDate = (time) => startDate.value ? moment(time).isBefore(startDate.value) || moment().isBefore(time) : moment().isBefore(time);
function formatData(list, idx) {
  list.forEach(item => {
    chartData[`chart${idx}`].labels.push(`${item.battgroupName}-#${item.monNum}`);
    chartData[`chart${idx}`].volDatas.push(item.monVol);
    chartData[`chart${idx}`].resDatas.push(item.monRes);
  });
}
const currType = ref(0);
const productList = ref([]);
const monCapList = ref([]);
const monVolList = ref([]);
const lastParams = ref({});
// 获取标称容量
  function getMonCapList() {
    getMonCapByUid().then((res) => {
      let { code, data, data2 } = res;
      let list = [];
      if (code && data) {
        list = data2;
      }
      monCapList.value = list;
    });
  }
  function getMonVolList() {
    getMonVol().then((res) => {
      let { code, data, data2 } = res;
      let list = [];
      if (code && data) {
        list = data2;
      }
      monVolList.value = list;
    });
  }
  function getList() {
        let loading = $loading();
    let params = {
      endTime: endDate.value ? endDate.value + ' 23:59:59' : moment().format('YYYY-MM-DD') + ' 23:59:59',
      startTime: startDate.value ? startDate.value + ' 00:00:00' : startDate0,
      product: product.value || undefined,
      moncapstd: moncapstd.value || undefined,
      monvolstd: monvolstd.value || undefined,
    };
    lastParams.value = params;
        getSingleStatistic(params)
            .then((res) => {
                let { code, data, data2 } = res;
                let gList = [];
        let bList = [];
        let dList = [];
                let gNum = 0;
        let bNum = 0;
        let dNum = 0;
        loading.close();
                if (code && data) {
          gList = data2.goodlist;
          bList = data2.badlist;
          dList = data2.damagelist;
          gNum = data2.goodlistNum;
          bNum = data2.badlistNum;
          dNum = data2.damagelistNum;
                }
        goodlistNum.value = gNum;
        badlistNum.value = bNum;
        damagelistNum.value = dNum;
        goodlist.value = gList;
        badlist.value = bList;
        damagelist.value = dList;
        formatData(gList, 0);
        formatData(bList, 1);
        formatData(dList, 2);
        updateBarChart();
        updateLineChart();
            })
            .catch((err) => {
                console.log(err);
                loading.close();
            });
    }
  function updateBarChart() {
    if (chart0.value) {
      chart0.value.updateChart(barLabels, [goodlistNum.value, badlistNum.value, damagelistNum.value]);
    }
  }
  function updateLineChart() {
    let idx = currType.value;
    if (chart1.value) {
      chart1.value.updateChart(chartData[`chart${idx}`].labels, chartData[`chart${idx}`].volDatas);
    }
    if (chart2.value) {
      chart2.value.updateChart(chartData[`chart${idx}`].labels, chartData[`chart${idx}`].resDatas);
    }
  }
  function getProductList() {
    getBatteryBrand().then((res) => {
      let { code, data, data2 } = res;
      let list = [];
      if (code && data) {
        list = data2;
      }
      productList.value = list;
    });
  }
  function createGraphByOpt (xLabels, datas) {
    xLabels = xLabels || [];
    datas = datas || [];
    allGraphInstance.clear();
    let lineStyle = {
      color: "#000",
    };
    let opt = {
      animation: false,
      grid: {
          left: '3%',
          right: '4%',
          bottom: '3%',
          top: 30,
          containLabel: true
        },
        xAxis: [{
          type: 'category',
          // boundaryGap: false,
          axisLine: {
            lineStyle
          },
          data: xLabels
        }],
        yAxis: [{
          type: 'value',
          axisTick: {
            show: true,
          },
          axisLine: {
            show: true,
            lineStyle
          },
          splitLine: {
            show: false
          }
        }],
        series: [{
          type: 'line',
          smooth: true,
          symbolSize: 0,
          lineStyle,
          data: datas
        }]
      };
    allGraphInstance.setOption(opt);
    return allGraphInstance.getDataURL({ pixelRatio: 1, backgroundColor: "#fff" });
  }
  async function exportFile () {
    let img0 = chart0.value.getChart().getDataURL({ pixelRatio: 1, backgroundColor: "#fff" });
    let params = {
      ...lastParams.value,
      topPic: img0,
      goodVolPic: createGraphByOpt(chartData.chart0.labels, chartData.chart0.volDatas),
      goodResPic: createGraphByOpt(chartData.chart0.labels, chartData.chart0.resDatas),
      badVolPic: createGraphByOpt(chartData.chart1.labels, chartData.chart1.volDatas),
      badResPic: createGraphByOpt(chartData.chart1.labels, chartData.chart1.resDatas),
      damageVolPic: createGraphByOpt(chartData.chart2.labels, chartData.chart2.volDatas),
      damageResPic: createGraphByOpt(chartData.chart2.labels, chartData.chart2.resDatas),
    };
    // console.log('params', 'export', params, '=============');
    let loading = $loading();
    try {
      let res = await battMonExport(params);
      // console.log('res', res, '=============');
      loading.close();
      const nameList = res.headers["content-disposition"];
      const fileName = nameList.split("=")[1];
      let blob = res.data;
      let url = window.URL.createObjectURL(blob);
      let a = document.createElement("a");
      a.href = url;
      a.download = decodeURIComponent(fileName);
      a.click();
      window.URL.revokeObjectURL(url);
    } catch (error) {
      loading.close();
      console.log('error', error, '=============');
      $message.error("导出失败");
    }
  }
  onMounted(() => {
    getProductList();
    getMonCapList();
    getMonVolList();
        getList();
    // 绑定事件
    if (chart0.value) {
      chart0.value.getChart().on('click', function (params) {
        // console.log('params', params, '=============');
        if (params.componentType == 'series') {
          currType.value = params.dataIndex;
          updateLineChart();
        }
      });
    }
    if (allGraph.value) {
      allGraphInstance = echarts.init(allGraph.value, 'transparent');
    }
    });
</script>
<template>
  <div class="page-wrapper">
    <!-- <div class="page-header">
    </div> -->
    <div class="page-content">
      <yc-card is-full>
        <div class="page-content-wrapper">
          <div class="page-content-tools page-filter">
            <div class="grid-container" :style="{'--counter': 8}">
              <div class="grid-item">
                <div class="label">品牌</div>
                <div class="value">
                  <el-select
                    v-model="product"
                    filterable
                    clearable
                    placeholder="请选择"
                    style="width: 180px"
                  >
                    <el-option
                      v-for="(item, idx) in productList"
                      :key="'list10_' + idx"
                      :label="item"
                      :value="item"
                    />
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">投运时间</div>
                <div class="value" style="grid-column: span 5;">
                  <el-date-picker
                    v-model="startDate"
                    type="date"
                    size="small"
                    placeholder="选择日期"
                    format="YYYY-MM-DD"
                    value-format="YYYY-MM-DD"
                    :disabled-date="startDisabledDate"
                  />
                  -
                  <el-date-picker
                    v-model="endDate"
                    size="small"
                    type="date"
                    placeholder="选择日期"
                    format="YYYY-MM-DD"
                    value-format="YYYY-MM-DD"
                    :disabled-date="endDisabledDate"
                  />
                </div>
              </div>
              <div class="grid-item">
                <div class="label">标称容量</div>
                <div class="value">
                  <el-select
                    v-model="moncapstd"
                    filterable
                    clearable
                    placeholder="请选择"
                    style="width: 180px"
                  >
                    <el-option
                      v-for="(item, idx) in monCapList"
                      :key="'list11_' + idx"
                      :label="item"
                      :value="item"
                    />
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">电压等级</div>
                <div class="value">
                  <el-select
                    v-model="monvolstd"
                    filterable
                    clearable
                    placeholder="请选择"
                    style="width: 180px"
                  >
                    <el-option
                      v-for="(item, idx) in monVolList"
                      :key="'list9_' + idx"
                      :label="item"
                      :value="item"
                    />
                  </el-select>
                </div>
                <div class="btn-grp">
                  <el-button type="primary" size="small" @click="getList">查询</el-button>
                  <el-button type="warning" size="small" @click="exportFile">导出</el-button>
                </div>
              </div>
            </div>
          </div>
          <div class="page-content-table">
            <div class="pos-rel">
              <div class="pos-abs">
                <div class="left">
                  <card title="单体优秀/劣化/损坏统计图">
                    <bar-chart ref="chart0"></bar-chart>
                  </card>
                </div>
                <div class="right">
                  <div class="top">
                    <card title="浮充电压">
                      <line-chart ref="chart1"></line-chart>
                    </card>
                  </div>
                  <div class="bottom">
                    <card title="单体内阻">
                      <line-chart ref="chart2"></line-chart>
                    </card>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </yc-card>
    </div>
    <div class="page-footer"></div>
    <div class="allGraph">
      <div class="chart-contain">
        <div class="chart" ref="allGraph"></div>
      </div>
    </div>
  </div>
</template>
<style scoped lang="less">
.page-wrapper {
  display: flex;
  flex-direction: row;
  // padding: 8px;
  height: 100%;
  .page-content {
    flex: 1;
    padding-bottom: 8px;
  }
}
.page-content-wrapper {
  display: flex;
  flex-direction: column;
  height: 100%;
  .page-content-tools {
    padding-bottom: 8px;
    .flex-row {
      display: flex;
      align-items: center;
      :deep(.el-checkbox) {
        color: #fff;
      }
      .group {
        margin-left: 30px;
      }
    }
  }
  .page-content-table {
    // border-top: 1px solid var(--border-light-color);
    box-sizing: border-box;
    flex: 1;
    margin-left: 26px;
    margin-right: 26px;
  }
  .page-content-page {
    padding: 8px 8px 0 8px;
    text-align: center;
    .el-page-container {
      display: inline-block;
      padding: 0 16px;
    }
    .page-tool {
      display: inline-block;
    }
  }
}
.hdw-card-container {
  width: 240px;
  padding-right: 8px;
  height: 100%;
}
.pos-rel {
  position: relative;
  width: 100%;
  height: 100%;
  .pos-abs {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    height: 100%;
    display: flex;
    .left {
      width: 400px;
      margin-right: 10px;
    }
    .right {
      flex: 1;
      display: flex;
      flex-direction: column;
      .top {
        flex: 1;
      }
      .bottom {
        margin-top: 10px;
        flex: 1;
      }
    }
  }
}
.allGraph {
  position: absolute;
  top: -1000px;
  left: 0;
  width: 600px;
  height: 400px;
  .chart-contain {
    height: 100%;
    width: 100%;
    .chart {
      height: 100%;
      width: 100%;
    }
  }
}
.tools-filter {
  display: inline-block;
  font-size: 14px;
  .tools-filter-item {
    display: inline-block;
    margin-right: 8px;
    .filter-label {
      display: inline-block;
    }
    .filter-content {
      display: inline-block;
    }
  }
}
</style>
src/views/statistics/battCompare0.vue
New file
@@ -0,0 +1,546 @@
<script setup name="battCompare0">
    import { ref, reactive, onMounted, computed, nextTick } from "vue";
    import { storeToRefs } from "pinia";
    import { Search, Plus } from "@element-plus/icons-vue";
    import ycCard from "@/components/ycCard/index.vue";
    import addEdit from "../datas/addEdit.vue";
    import { ElMessage } from "element-plus";
    import useElement from "@/hooks/useElement.js";
  import { useUserStore } from '@/store/user';
  import useStation from "@/hooks/useStationList.js";
  const { provice, city, country, stationName,
    proviceList, cityList, countryList, stationList,
  } = useStation();
  import { useRouter } from "vue-router";
  const router = useRouter();
  import { ExportFile } from '@/utils/exportFile.js';
  import powerTypes from '@/utils/const/const_powerType.js';
  import hrTypes from '@/utils/const/const_hrTestType.js';
  import {
    getBattPerformance,
    getBatteryBrand,
  } from "@/api/station";
  const userStore = useUserStore();
  const { uid, uname } = storeToRefs(userStore);
  import moment from 'moment';
  import formatSeconds from '@/utils/formatSeconds';
  import {
    toFixed,
    digits,
  } from '@/utils/toFixed';
  import {
    getBattCompare15,
  } from "@/api/statistic.js";
    const { $loading, $message, $confirm } = useElement();
    const headers = [
    {
      prop: "stationName",
      label: "站点名称",
      width: "160",
    },
    {
      prop: "devName",
      label: "设备名称",
      width: "80",
    },
    {
      prop: "battgroupName",
      label: "电池组名称",
      width: "80",
    },
    {
      prop: "product",
      label: "电池品牌",
      width: "100",
    },
    {
      prop: "inuseTime",
      label: "投运时间",
      width: "100",
    },
    {
      prop: "monvolstd",
      label: "电池规格",
      width: "100",
    },
    {
      prop: "monNumsStr",
      label: "落后单体编号",
      width: "160",
    },
    {
      prop: "realCap",
      label: "预估容量",
      width: "80",
    },
    {
      prop: "precentCapStr",
      label: "容量百分比",
      width: "80",
    },
    {
      prop: "capperformance",
      label: "电池性能",
      width: "80",
    },
  ];
   const testType = ref('');
  const hrTypeList = computed(() => {
    return Object.keys(hrTypes).map(v => {
      return {
        value: v,
        label: hrTypes[v],
      };
    });
  });
    const background = ref(true);
    const disabled = ref(false);
    const pageCurr = ref(1);
    const pageSize = ref(10);
    const total = ref(0);
    const addEditVisible = ref(false);
    const dialogTitle = ref("");
    const currentAreaId = ref();
    const currentAreaIds = ref([]);
  const testStartDate = ref('');
  const testEndDate = ref('');
    const datas = reactive({
        tableData: [],
        rowData: {},
    });
  // 计算属性生成 picker-options(更简洁)
  const startDisabledDate = (time) =>  testEndDate.value ? moment(testEndDate.value).isBefore(time) || moment().isBefore(time) : moment().isBefore(time);
  const endDisabledDate = (time) => testStartDate.value ? moment(time).isBefore(testStartDate.value) || moment().isBefore(time) : moment().isBefore(time);
  const performance = ref('');
  const performanceList = ref([]);
  function getPerformancList() {
    getBattPerformance()
      .then((res) => {
        let { code, data, data2 } = res;
        let list = [];
        if (code && data) {
          // console.log(data);
          list = Object.keys(data2).map((key) => ({ value: key, label: data2[key] }));
        }
        performanceList.value = list;
      })
      .catch((err) => {
        console.log(err);
      });
  }
  const product = ref('');
  const productList = ref([]);
  function getProductList() {
    getBatteryBrand().then((res) => {
      let { code, data, data2 } = res;
      let list = [];
      if (code && data) {
        list = data2;
      }
      productList.value = list;
    });
  }
    function getList() {
        let loading = $loading();
    let params = {
      provice: provice.value || undefined,
      city: city.value || undefined,
      country: country.value || undefined,
      stationName: stationName.value || undefined,
      pageNum: pageCurr.value,
      pageSize: pageSize.value,
      performance: performance.value || undefined,
      product: product.value || undefined,
      testStartTime: testStartDate.value ? testStartDate.value + ' 00:00:00' : undefined,
      testEndTime: testEndDate.value ? testEndDate.value + ' 23:59:59' : undefined,
    };
        getBattCompare15(params)
            .then((res) => {
                let { code, data, data2 } = res;
                let list = [];
                let _total = 0;
                if (code && data) {
                    // console.log(data);
                    list = data2.list.map(v => ({
            ...v,
            monNumsStr: v.monNums.map(v => `#${v}`).join(',') || '--',
            precentCapStr: toFixed(v.precentCap, digits.PREC) + '%',
          }));
                    _total = data2.total;
                }
                loading.close();
                // tableData.length = 0;
                // tableData.push(...list);
                datas.tableData = list;
                total.value = _total;
            })
            .catch((err) => {
                console.log(err);
                loading.close();
            });
    }
    // 展示数据数量
    function handleSizeChange(val) {
        pageSize.value = val;
        getList();
    }
    // 翻页
    function handleCurrentChange(val) {
        pageCurr.value = val;
        getList();
    }
  function filterChange() {
    nextTick(() => {
      pageCurr.value = 1;
      getList();
    });
  }
  function goRt (row) {
    router.push({
      path: '/datas/realtime',
      query: {
        stationId: row.stationId || undefined,
        powerId: row.powerId || undefined,
        devId: row.devId || undefined,
        battgroupId: row.battgroupId || undefined,
        pageFlag: Math.random(),
      }
    });
  }
  function goHis (row) {
    router.push({
      path: '/datas/history',
      query: {
        stationId: row.stationId || undefined,
        powerId: row.powerId || undefined,
        devId: row.devId || undefined,
        battgroupId: row.battgroupId || undefined,
        pageTab: 'his-real',
        pageFlag: Math.random(),
      }
    });
  }
  function exportExcel() {
    ExportFile(headers, datas.tableData, "蓄电池组对比分析--同一品牌同一时间");
  }
    onMounted(() => {
    getPerformancList();
    getProductList();
        getList();
    });
</script>
<template>
  <div class="page-wrapper">
    <!-- <div class="page-header">
    </div> -->
    <div class="page-content">
      <yc-card is-full>
        <div class="page-content-wrapper">
          <div class="page-content-tools page-filter">
            <div class="grid-container" :style="{'--counter': 10}">
              <div class="grid-item">
                <div class="label">省</div>
                <div class="value">
                  <el-select
                    v-model="provice"
                    size="small"
                    clearable
                    @change="filterChange"
                    placeholder="请选择省"
                  >
                    <el-option
                      v-for="item in proviceList"
                      :key="'l0_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">市</div>
                <div class="value">
                  <el-select
                    v-model="city"
                    size="small"
                    clearable
                    @change="filterChange"
                    placeholder="请选择市"
                  >
                    <el-option
                      v-for="item in cityList"
                      :key="'l1_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">区县</div>
                <div class="value">
                  <el-select
                    v-model="country"
                    clearable
                    @change="filterChange"
                    size="small"
                    placeholder="请选择区县"
                  >
                    <el-option
                      v-for="item in countryList"
                      :key="'l2_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">站点</div>
                <div class="value">
                  <el-select
                    v-model="stationName"
                    clearable
                    @change="filterChange"
                    size="small"
                    placeholder="请选择站点"
                  >
                    <el-option
                      v-for="item in stationList"
                      :key="'l3_' + item.stationId"
                      :label="item.stationName"
                      :value="item.stationName"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">电池品牌</div>
                <div class="value">
                  <el-select
                    v-model="performance"
                    filterable
                    clearable
                    size="small"
                    @change="filterChange"
                    placeholder="请选择"
                  >
                    <el-option
                      v-for="(item, idx) in performanceList"
                      :key="'list7_' + idx"
                      :label="item.label"
                      :value="item.value"
                    />
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">电池性能</div>
                <div class="value">
                  <el-select
                    v-model="product"
                    filterable
                    clearable
                    size="small"
                    @change="filterChange"
                    placeholder="请选择"
                  >
                    <el-option
                      v-for="(item, idx) in productList"
                      :key="'list6_' + idx"
                      :label="item"
                      :value="item"
                    />
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">统计时间段</div>
                <div class="value" style="grid-column: span 7;">
                  <el-date-picker
                    v-model="testStartDate"
                    type="date"
                    size="small"
                    placeholder="选择日期"
                    format="YYYY-MM-DD"
                    value-format="YYYY-MM-DD"
                    :disabled-date="startDisabledDate"
                  />
                  -
                  <el-date-picker
                    v-model="testEndDate"
                    size="small"
                    type="date"
                    placeholder="选择日期"
                    format="YYYY-MM-DD"
                    value-format="YYYY-MM-DD"
                    :disabled-date="endDisabledDate"
                  />
                </div>
              </div>
            </div>
          </div>
          <div class="page-content-table">
            <div class="pos-rel">
              <div class="pos-abs">
                <el-table class="yc-table" stripe height="100%" size="small" :data="datas.tableData" style="width: 100%">
                  <el-table-column type="index" fixed="left" label="序号" width="60"></el-table-column>
                  <el-table-column v-for="header in headers" :key="header.prop" :prop="header.prop" :label="header.label"
                    :min-width="header.width" align="center">
                    <template #default="scope">{{ scope.row[header.prop] }}</template>
                  </el-table-column>
                  <el-table-column label="操作" fixed="right" width="240" align="center">
                    <template #default="scope">
                      <el-button type="warning" size="small" @click="goRt(scope.row)">实时监测</el-button>
                      <el-button type="primary" size="small" @click="goHis(scope.row)">历史数据</el-button>
                    </template>
                  </el-table-column>
                </el-table>
              </div>
            </div>
          </div>
          <div class="page-content-page">
            <div class="page-tool">
              <el-button type="primary" round size="small" @click="getList" :icon="Search">查询</el-button>
            </div>
            <div class="el-page-container">
              <el-pagination v-model:current-page="pageCurr" v-model:page-size="pageSize"
                :page-sizes="[20, 40, 60, 80, 100, 200, 300, 400]" size="small" :disabled="disabled"
                :background="background" layout="total, sizes, prev, pager, next, jumper" :total="total"
                @size-change="handleSizeChange" @current-change="handleCurrentChange" />
            </div>
            <div class="page-tool">
              <el-button type="primary" round size="small" @click="exportExcel">导出</el-button>
            </div>
          </div>
        </div>
      </yc-card>
    </div>
    <div class="page-footer"></div>
  </div>
</template>
<style scoped lang="less">
.page-wrapper {
  display: flex;
  flex-direction: row;
  // padding: 8px;
  height: 100%;
  .page-content {
    flex: 1;
  }
}
.page-content-wrapper {
  display: flex;
  flex-direction: column;
  height: 100%;
  .page-content-tools {
    padding-bottom: 8px;
  }
  .page-content-table {
    // border-top: 1px solid var(--border-light-color);
    box-sizing: border-box;
    flex: 1;
    margin-left: 26px;
    margin-right: 26px;
  }
  .page-content-page {
    padding: 8px 8px 0 8px;
    text-align: center;
    .el-page-container {
      display: inline-block;
      padding: 0 16px;
    }
    .page-tool {
      display: inline-block;
    }
  }
}
.hdw-card-container {
  width: 240px;
  padding-right: 8px;
  height: 100%;
}
.pos-rel {
  position: relative;
  width: 100%;
  height: 100%;
  .pos-abs {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    height: 100%;
  }
}
.tools-filter {
  display: inline-block;
  font-size: 14px;
  .tools-filter-item {
    display: inline-block;
    margin-right: 8px;
    .filter-label {
      display: inline-block;
    }
    .filter-content {
      display: inline-block;
    }
  }
}
.max-width {
  max-width: 200px;
}
</style>
src/views/statistics/battCompare1.vue
New file
@@ -0,0 +1,594 @@
<script setup name="battCompare1">
  import { ref, reactive, onMounted, computed, nextTick } from "vue";
  import { storeToRefs } from "pinia";
  import { Search, Plus } from "@element-plus/icons-vue";
  import ycCard from "@/components/ycCard/index.vue";
  import addEdit from "../datas/addEdit.vue";
  import { ElMessage } from "element-plus";
  import useElement from "@/hooks/useElement.js";
  import { useUserStore } from '@/store/user';
  import useStation from "@/hooks/useStationList.js";
  const { provice, city, country, stationName,
    proviceList, cityList, countryList, stationList,
  } = useStation();
  import { useRouter } from "vue-router";
  const router = useRouter();
  import { ExportFile } from '@/utils/exportFile.js';
  import powerTypes from '@/utils/const/const_powerType.js';
  import hrTypes from '@/utils/const/const_hrTestType.js';
  import {
    getBattPerformance,
    getMonCapByUid,
    getBatteryBrand,
  } from "@/api/station";
  const userStore = useUserStore();
  const { uid, uname } = storeToRefs(userStore);
  import moment from 'moment';
  import formatSeconds from '@/utils/formatSeconds';
  import {
    toFixed,
    digits,
  } from '@/utils/toFixed';
  import {
    getBattCompare16,
  } from "@/api/statistic.js";
  const { $loading, $message, $confirm } = useElement();
  const headers = [
    {
      prop: "stationName",
      label: "站点名称",
      width: "160",
    },
    {
      prop: "devName",
      label: "设备名称",
      width: "80",
    },
    {
      prop: "battgroupName",
      label: "电池组名称",
      width: "80",
    },
    {
            prop: "product",
            label: "电池品牌",
            width: "100",
        },
    {
            prop: "inuseTime",
            label: "投运时间",
            width: "100",
        },
    {
            prop: "moncapstd",
            label: "标准容量",
            width: "100",
        },
    {
            prop: "monvolstd",
            label: "标称电压",
            width: "100",
        },
    {
      prop: "realCap",
            label: "预估实际容量",
            width: "80",
        },
    {
      prop: "stopReason",
      label: "停止原因",
      width: "160",
    },
    {
            prop: "precentCapStr",
            label: "容量百分比",
            width: "80",
        },
    {
      prop: "capperformance",
      label: "评分结果",
      width: "80",
    },
    {
      prop: "capperformance",
      label: "电池性能",
      width: "80",
    },
    ];
   const testType = ref('');
  const hrTypeList = computed(() => {
    return Object.keys(hrTypes).map(v => {
      return {
        value: v,
        label: hrTypes[v],
      };
    });
  });
  const background = ref(true);
  const disabled = ref(false);
  const pageCurr = ref(1);
  const pageSize = ref(10);
  const total = ref(0);
  const addEditVisible = ref(false);
  const dialogTitle = ref("");
  const currentAreaId = ref();
  const currentAreaIds = ref([]);
  const inuseStartTime = ref('');
  const inuseEndTime = ref('');
  const datas = reactive({
    tableData: [],
    rowData: {},
  });
  // 计算属性生成 picker-options(更简洁)
  const startDisabledDate = (time) =>  inuseEndTime.value ? moment(inuseEndTime.value).isBefore(time) || moment().isBefore(time) : moment().isBefore(time);
  const endDisabledDate = (time) => inuseStartTime.value ? moment(time).isBefore(inuseStartTime.value) || moment().isBefore(time) : moment().isBefore(time);
  const performance = ref('');
  const performanceList = ref([]);
  function getPerformancList() {
    getBattPerformance()
      .then((res) => {
        let { code, data, data2 } = res;
        let list = [];
        if (code && data) {
          // console.log(data);
          list = Object.keys(data2).map((key) => ({ value: key, label: data2[key] }));
        }
        performanceList.value = list;
      })
      .catch((err) => {
        console.log(err);
      });
  }
  const product = ref('');
  const productList = ref([]);
  function getProductList() {
    getBatteryBrand().then((res) => {
      let { code, data, data2 } = res;
      let list = [];
      if (code && data) {
        list = data2;
      }
      productList.value = list;
    });
  }
  const moncapstd = ref('');
  const monCapList = ref([]);
// 获取标称容量
  function getMonCapList() {
    getMonCapByUid().then((res) => {
      let { code, data, data2 } = res;
      let list = [];
      if (code && data) {
        list = data2;
      }
      monCapList.value = list;
    });
  }
  function getList() {
    let loading = $loading();
    let params = {
      provice: provice.value || undefined,
      city: city.value || undefined,
      country: country.value || undefined,
      stationName: stationName.value || undefined,
      pageNum: pageCurr.value,
      pageSize: pageSize.value,
      performance: performance.value || undefined,
      product: product.value || undefined,
      inuseStartTime: inuseStartTime.value ? inuseStartTime.value + ' 00:00:00' : undefined,
      inuseEndTime: inuseEndTime.value ? inuseEndTime.value + ' 23:59:59' : undefined,
    };
    getBattCompare16(params)
      .then((res) => {
        let { code, data, data2 } = res;
        let list = [];
        let _total = 0;
        if (code && data) {
          // console.log(data);
          list = data2.list.map(v => ({
            ...v,
            precentCapStr: toFixed(v.precentCap, digits.PREC) + '%',
          }));
          _total = data2.total;
        }
        loading.close();
        // tableData.length = 0;
        // tableData.push(...list);
        datas.tableData = list;
        total.value = _total;
      })
      .catch((err) => {
        console.log(err);
        loading.close();
      });
  }
  // 展示数据数量
  function handleSizeChange(val) {
    pageSize.value = val;
    getList();
  }
  // 翻页
  function handleCurrentChange(val) {
    pageCurr.value = val;
    getList();
  }
  function filterChange() {
    nextTick(() => {
      pageCurr.value = 1;
      getList();
    });
  }
  function goRt (row) {
    router.push({
      path: '/datas/realtime',
      query: {
        stationId: row.stationId || undefined,
        powerId: row.powerId || undefined,
        devId: row.devId || undefined,
        battgroupId: row.battgroupId || undefined,
        pageFlag: Math.random(),
      }
    });
  }
  function goHis (row) {
    router.push({
      path: '/datas/history',
      query: {
        stationId: row.stationId || undefined,
        powerId: row.powerId || undefined,
        devId: row.devId || undefined,
        battgroupId: row.battgroupId || undefined,
        pageTab: 'his-real',
        pageFlag: Math.random(),
      }
    });
  }
  function exportExcel() {
    ExportFile(headers, datas.tableData, "蓄电池组对比分析--同一品牌同一时间");
  }
  onMounted(() => {
    getPerformancList();
    getProductList();
    getMonCapList();
    getList();
  });
</script>
<template>
  <div class="page-wrapper">
    <!-- <div class="page-header">
    </div> -->
    <div class="page-content">
      <yc-card is-full>
        <div class="page-content-wrapper">
          <div class="page-content-tools page-filter">
            <div class="grid-container" :style="{'--counter': 6}">
              <div class="grid-item">
                <div class="label">省</div>
                <div class="value">
                  <el-select
                    v-model="provice"
                    size="small"
                    clearable
                    @change="filterChange"
                    placeholder="请选择省"
                  >
                    <el-option
                      v-for="item in proviceList"
                      :key="'l0_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">市</div>
                <div class="value">
                  <el-select
                    v-model="city"
                    size="small"
                    clearable
                    @change="filterChange"
                    placeholder="请选择市"
                  >
                    <el-option
                      v-for="item in cityList"
                      :key="'l1_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">区县</div>
                <div class="value">
                  <el-select
                    v-model="country"
                    clearable
                    @change="filterChange"
                    size="small"
                    placeholder="请选择区县"
                  >
                    <el-option
                      v-for="item in countryList"
                      :key="'l2_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">站点</div>
                <div class="value">
                  <el-select
                    v-model="stationName"
                    clearable
                    @change="filterChange"
                    size="small"
                    placeholder="请选择站点"
                  >
                    <el-option
                      v-for="item in stationList"
                      :key="'l3_' + item.stationId"
                      :label="item.stationName"
                      :value="item.stationName"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">电池品牌</div>
                <div class="value">
                  <el-select
                    v-model="performance"
                    filterable
                    clearable
                    size="small"
                    @change="filterChange"
                    placeholder="请选择"
                  >
                    <el-option
                      v-for="(item, idx) in performanceList"
                      :key="'list7_' + idx"
                      :label="item.label"
                      :value="item.value"
                    />
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">标称容量</div>
                <div class="value">
                  <el-select
                    v-model="moncapstd"
                    filterable
                    clearable
                    placeholder="请选择"
                  >
                    <el-option
                      v-for="(item, idx) in monCapList"
                      :key="'list11_' + idx"
                      :label="item"
                      :value="item"
                    />
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">电池性能</div>
                <div class="value">
                  <el-select
                    v-model="product"
                    filterable
                    clearable
                    size="small"
                    @change="filterChange"
                    placeholder="请选择"
                  >
                    <el-option
                      v-for="(item, idx) in productList"
                      :key="'list6_' + idx"
                      :label="item"
                      :value="item"
                    />
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">投运时间段</div>
                <div class="value" style="grid-column: span 7;">
                  <el-date-picker
                    v-model="inuseStartTime"
                    type="date"
                    size="small"
                    placeholder="选择日期"
                    format="YYYY-MM-DD"
                    value-format="YYYY-MM-DD"
                    :disabled-date="startDisabledDate"
                  />
                  -
                  <el-date-picker
                    v-model="inuseEndTime"
                    size="small"
                    type="date"
                    placeholder="选择日期"
                    format="YYYY-MM-DD"
                    value-format="YYYY-MM-DD"
                    :disabled-date="endDisabledDate"
                  />
                </div>
              </div>
              <div class="grid-item">
                <el-button type="primary" size="small" >设置权重</el-button>
              </div>
            </div>
          </div>
          <div class="page-content-table">
            <div class="pos-rel">
              <div class="pos-abs">
                <el-table class="yc-table" stripe height="100%" size="small" :data="datas.tableData" style="width: 100%">
                  <el-table-column type="index" fixed="left" label="序号" width="60"></el-table-column>
                  <el-table-column v-for="header in headers" :key="header.prop" :prop="header.prop" :label="header.label"
                    :min-width="header.width" align="center">
                    <template #default="scope">{{ scope.row[header.prop] }}</template>
                  </el-table-column>
                  <el-table-column label="操作" fixed="right" width="240" align="center">
                    <template #default="scope">
                      <el-button type="warning" size="small" @click="goRt(scope.row)">实时监测</el-button>
                      <el-button type="primary" size="small" @click="goHis(scope.row)">历史数据</el-button>
                    </template>
                  </el-table-column>
                </el-table>
              </div>
            </div>
          </div>
          <div class="page-content-page">
            <div class="page-tool">
              <el-button type="primary" round size="small" @click="getList" :icon="Search">查询</el-button>
            </div>
            <div class="el-page-container">
              <el-pagination v-model:current-page="pageCurr" v-model:page-size="pageSize"
                :page-sizes="[20, 40, 60, 80, 100, 200, 300, 400]" size="small" :disabled="disabled"
                :background="background" layout="total, sizes, prev, pager, next, jumper" :total="total"
                @size-change="handleSizeChange" @current-change="handleCurrentChange" />
            </div>
            <div class="page-tool">
              <el-button type="primary" round size="small" @click="exportExcel">导出</el-button>
            </div>
          </div>
        </div>
      </yc-card>
    </div>
    <div class="page-footer"></div>
  </div>
</template>
<style scoped lang="less">
.page-wrapper {
  display: flex;
  flex-direction: row;
  // padding: 8px;
  height: 100%;
  .page-content {
    flex: 1;
  }
}
.page-content-wrapper {
  display: flex;
  flex-direction: column;
  height: 100%;
  .page-content-tools {
    padding-bottom: 8px;
  }
  .page-content-table {
    // border-top: 1px solid var(--border-light-color);
    box-sizing: border-box;
    flex: 1;
    margin-left: 26px;
    margin-right: 26px;
  }
  .page-content-page {
    padding: 8px 8px 0 8px;
    text-align: center;
    .el-page-container {
      display: inline-block;
      padding: 0 16px;
    }
    .page-tool {
      display: inline-block;
    }
  }
}
.hdw-card-container {
  width: 240px;
  padding-right: 8px;
  height: 100%;
}
.pos-rel {
  position: relative;
  width: 100%;
  height: 100%;
  .pos-abs {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    height: 100%;
  }
}
.tools-filter {
  display: inline-block;
  font-size: 14px;
  .tools-filter-item {
    display: inline-block;
    margin-right: 8px;
    .filter-label {
      display: inline-block;
    }
    .filter-content {
      display: inline-block;
    }
  }
}
.max-width {
  max-width: 200px;
}
</style>
src/views/statistics/device.vue
New file
@@ -0,0 +1,422 @@
<script setup name="deviceStatistics">
    import { ref, reactive, onMounted, computed, nextTick } from "vue";
    import { storeToRefs } from "pinia";
    import { Search, Plus } from "@element-plus/icons-vue";
    import ycCard from "@/components/ycCard/index.vue";
    import addEdit from "../datas/addEdit.vue";
    import { ElMessage } from "element-plus";
    import useElement from "@/hooks/useElement.js";
  import { useUserStore } from '@/store/user';
  import useStation from "@/hooks/useStationList.js";
  const { provice, city, country, stationName,
    proviceList, cityList, countryList, stationList,
  } = useStation();
  import { useRouter } from "vue-router";
  const router = useRouter();
  import { ExportFile } from '@/utils/exportFile.js';
  import powerTypes from '@/utils/const/const_powerType.js';
  import hrTypes from '@/utils/const/const_hrTestType.js';
  import {
    delBatt,
  } from "@/api/station";
  const userStore = useUserStore();
  const { uid, uname } = storeToRefs(userStore);
  import moment from 'moment';
  import formatSeconds from '@/utils/formatSeconds';
  import {
    toFixed,
    digits,
  } from '@/utils/toFixed';
  import {
    getDeviceStatistic,
  } from "@/api/statistic.js";
    const { $loading, $message, $confirm } = useElement();
    const headers = [
        {
            prop: "provice",
            label: "省",
            width: "100",
        },
    {
            prop: "city",
            label: "市",
            width: "100",
        },
    {
            prop: "country",
            label: "区县",
            width: "100",
        },
    {
            prop: "fullName",
            label: "站点名称",
            width: "160",
        },
    {
      prop: "devName",
            label: "设备名称",
            width: "80",
        },
    {
            prop: "stationType",
            label: "电压等级",
            width: "80",
        },
    {
            prop: "battCount",
            label: "电池组数",
            width: "80",
        },
    ];
   const testType = ref('');
  const hrTypeList = computed(() => {
    return Object.keys(hrTypes).map(v => {
      return {
        value: v,
        label: hrTypes[v],
      };
    });
  });
    const background = ref(true);
    const disabled = ref(false);
    const pageCurr = ref(1);
    const pageSize = ref(10);
    const total = ref(0);
    const addEditVisible = ref(false);
    const dialogTitle = ref("");
    const currentAreaId = ref();
    const currentAreaIds = ref([]);
  const testStartDate = ref('');
  const testEndDate = ref('');
    const datas = reactive({
        tableData: [],
        rowData: {},
    });
  // 计算属性生成 picker-options(更简洁)
  const startDisabledDate = (time) =>  testEndDate.value ? moment(testEndDate.value).isBefore(time) || moment().isBefore(time) : moment().isBefore(time);
  const endDisabledDate = (time) => testStartDate.value ? moment(time).isBefore(testStartDate.value) || moment().isBefore(time) : moment().isBefore(time);
    function getList() {
        let loading = $loading();
    let params = {
      provice: provice.value || undefined,
      city: city.value || undefined,
      country: country.value || undefined,
      stationName: stationName.value || undefined,
      pageNum: pageCurr.value,
      pageSize: pageSize.value,
    };
        getDeviceStatistic(params)
            .then((res) => {
                let { code, data, data2 } = res;
                let list = [];
                let _total = 0;
                if (code && data) {
                    // console.log(data);
                    list = data2.list.map(v => ({
            ...v,
          }));
                    _total = data2.total;
                }
                loading.close();
                // tableData.length = 0;
                // tableData.push(...list);
                datas.tableData = list;
                total.value = _total;
            })
            .catch((err) => {
                console.log(err);
                loading.close();
            });
    }
    // 展示数据数量
    function handleSizeChange(val) {
        pageSize.value = val;
        getList();
    }
    // 翻页
    function handleCurrentChange(val) {
        pageCurr.value = val;
        getList();
    }
  function filterChange() {
    nextTick(() => {
      pageCurr.value = 1;
      getList();
    });
  }
  function goRt (row) {
    router.push({
      path: '/datas/realtime',
      query: {
        stationId: row.stationId || undefined,
        powerId: row.powerId || undefined,
        devId: row.devId || undefined,
        battgroupId: row.battgroupId || undefined,
        pageFlag: Math.random(),
      }
    });
  }
  function goHis (row) {
    router.push({
      path: '/datas/realtime',
      query: {
        stationId: row.stationId || undefined,
        powerId: row.powerId || undefined,
        devId: row.devId || undefined,
        battgroupId: row.battgroupId || undefined,
        pageTab: 'his-real',
        pageFlag: Math.random(),
      }
    });
  }
  function exportExcel() {
    ExportFile(headers, datas.tableData, "设备信息统计");
  }
    onMounted(() => {
        getList();
    });
</script>
<template>
  <div class="page-wrapper">
    <!-- <div class="page-header">
    </div> -->
    <div class="page-content">
      <yc-card is-full>
        <div class="page-content-wrapper">
          <div class="page-content-tools page-filter">
            <div class="grid-container" :style="{'--counter': 5}">
              <div class="grid-item">
                <div class="label">省</div>
                <div class="value">
                  <el-select
                    v-model="provice"
                    size="small"
                    clearable
                    @change="filterChange"
                    placeholder="请选择省"
                  >
                    <el-option
                      v-for="item in proviceList"
                      :key="'l0_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">市</div>
                <div class="value">
                  <el-select
                    v-model="city"
                    size="small"
                    clearable
                    @change="filterChange"
                    placeholder="请选择市"
                  >
                    <el-option
                      v-for="item in cityList"
                      :key="'l1_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">区县</div>
                <div class="value">
                  <el-select
                    v-model="country"
                    clearable
                    @change="filterChange"
                    size="small"
                    placeholder="请选择区县"
                  >
                    <el-option
                      v-for="item in countryList"
                      :key="'l2_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="grid-item">
                <div class="label">站点</div>
                <div class="value">
                  <el-select
                    v-model="stationName"
                    clearable
                    @change="filterChange"
                    size="small"
                    placeholder="请选择站点"
                  >
                    <el-option
                      v-for="item in stationList"
                      :key="'l3_' + item.stationId"
                      :label="item.stationName"
                      :value="item.stationName"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
            </div>
          </div>
          <div class="page-content-table">
            <div class="pos-rel">
              <div class="pos-abs">
                <el-table class="yc-table" stripe height="100%" size="small" :data="datas.tableData" style="width: 100%">
                  <el-table-column type="index" fixed="left" label="序号" width="60"></el-table-column>
                  <el-table-column v-for="header in headers" :key="header.prop" :prop="header.prop" :label="header.label"
                    :min-width="header.width" align="center">
                    <template #default="scope">{{ scope.row[header.prop] }}</template>
                  </el-table-column>
                  <el-table-column label="操作" fixed="right" width="240" align="center">
                    <template #default="scope">
                      <el-button type="warning" size="small" @click="goRt(scope.row)">实时监测</el-button>
                      <el-button type="primary" size="small" @click="goHis(scope.row)">历史数据</el-button>
                    </template>
                  </el-table-column>
                </el-table>
              </div>
            </div>
          </div>
          <div class="page-content-page">
            <div class="page-tool">
              <el-button type="primary" round size="small" @click="getList" :icon="Search">查询</el-button>
            </div>
            <div class="el-page-container">
              <el-pagination v-model:current-page="pageCurr" v-model:page-size="pageSize"
                :page-sizes="[20, 40, 60, 80, 100, 200, 300, 400]" size="small" :disabled="disabled"
                :background="background" layout="total, sizes, prev, pager, next, jumper" :total="total"
                @size-change="handleSizeChange" @current-change="handleCurrentChange" />
            </div>
            <div class="page-tool">
              <el-button type="primary" round size="small" @click="exportExcel">导出</el-button>
            </div>
          </div>
        </div>
      </yc-card>
    </div>
    <div class="page-footer"></div>
  </div>
</template>
<style scoped lang="less">
.page-wrapper {
  display: flex;
  flex-direction: row;
  // padding: 8px;
  height: 100%;
  .page-content {
    flex: 1;
  }
}
.page-content-wrapper {
  display: flex;
  flex-direction: column;
  height: 100%;
  .page-content-tools {
    padding-bottom: 8px;
  }
  .page-content-table {
    // border-top: 1px solid var(--border-light-color);
    box-sizing: border-box;
    flex: 1;
    margin-left: 26px;
    margin-right: 26px;
  }
  .page-content-page {
    padding: 8px 8px 0 8px;
    text-align: center;
    .el-page-container {
      display: inline-block;
      padding: 0 16px;
    }
    .page-tool {
      display: inline-block;
    }
  }
}
.hdw-card-container {
  width: 240px;
  padding-right: 8px;
  height: 100%;
}
.pos-rel {
  position: relative;
  width: 100%;
  height: 100%;
  .pos-abs {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    height: 100%;
  }
}
.tools-filter {
  display: inline-block;
  font-size: 14px;
  .tools-filter-item {
    display: inline-block;
    margin-right: 8px;
    .filter-label {
      display: inline-block;
    }
    .filter-content {
      display: inline-block;
    }
  }
}
.max-width {
  max-width: 200px;
}
</style>
src/views/statistics/power.vue
@@ -1,5 +1,5 @@
<script setup name="power">
    import { ref, reactive, onMounted, computed } from "vue";
    import { ref, reactive, onMounted, computed, nextTick } from "vue";
    import { storeToRefs } from "pinia";
    import { Search, Plus } from "@element-plus/icons-vue";
    import ycCard from "@/components/ycCard/index.vue";
@@ -13,139 +13,104 @@
    proviceList, cityList, countryList, stationList,
  } = useStation();
  import { useRouter } from "vue-router";
  const router = useRouter();
  import { ExportFile } from '@/utils/exportFile.js';
  import powerTypes from '@/utils/const/const_powerType.js';
  import hrTypes from '@/utils/const/const_hrTestType.js';
  import {
    delBatt,
  } from "@/api/station";
  const userStore = useUserStore();
  const { uid, uname } = storeToRefs(userStore);
  import moment from 'moment';
  import formatSeconds from '@/utils/formatSeconds';
  import {
    toFixed,
    digits,
  } from '@/utils/toFixed';
  import {
    getDevList,
  } from "@/api/station";
    getBattTinfStatistic,
  } from "@/api/statistic.js";
    const { $loading, $message, $confirm } = useElement();
    const headers = [
    {
            prop: "provice",
            label: "省",
            width: "80",
        },
    {
            prop: "city",
            label: "市",
            width: "80",
        },
    {
            prop: "country",
            label: "区县",
            width: "80",
        },
        {
            prop: "stationName",
            label: "机房名称",
            prop: "fullName",
            label: "站点名称",
            width: "160",
        },
    {
            prop: "stationType",
            label: "电压等级",
            width: "80",
        },
    {
            prop: "longitude",
            label: "经度",
            width: "80",
        },
    {
            prop: "latitude",
            label: "纬度",
            width: "80",
        },
    {
            prop: "powerName",
            label: "电源名称",
            width: "80",
        },
    {
            prop: "powerTypeStr",
            label: "电源类型",
            width: "80",
        },
    {
            prop: "company",
            label: "电源品牌",
            width: "80",
        },
        {
            prop: "powerModel",
            label: "电源型号",
            width: "80",
        },
    {
            prop: "protocol",
            label: "电源协议",
            width: "80",
        },
    {
            prop: "powerIp",
            label: "电源IP",
            width: "120",
        },
    {
            prop: "devName",
            label: "设备名称",
            width: "120",
        },
    {
            prop: "devType",
            label: "设备型号",
            width: "120",
        },
    {
            prop: "devIp",
            label: "设备IP",
            width: "120",
        },
    {
            prop: "battgroupName",
            label: "电池组名称",
            width: "120",
            width: "80",
        },
    {
            prop: "moncount",
            label: "电池单体个数",
            width: "120",
            prop: "testType",
            label: "测试类型",
            width: "80",
        },
    {
            prop: "monvolstd",
            label: "电池标称电压",
            width: "120",
            prop: "testStarttime",
            label: "测试开始时间",
            width: "80",
        },
    {
            prop: "moncapstd",
            label: "电池标称容量",
            width: "120",
      prop: "testCurr",
      label: "测试电流",
      width: "80",
    },
    {
      prop: "testCap",
      label: "测试容量",
      width: "80",
    },
    {
      prop: "testTimelongStr",
      label: "测试时长",
      width: "80",
    },
    {
            prop: "testStoptype",
            label: "停止原因",
            width: "80",
        },
    {
            prop: "monresstd",
            label: "电池标称内阻",
            width: "120",
            prop: "restCap",
            label: "预估实际容量",
            width: "80",
        },
    {
            prop: "product",
            label: "电池品牌",
            width: "120",
            prop: "precentCapStr",
            label: "容量百分比",
            width: "80",
        },
    {
            prop: "battModel",
            label: "电池型号",
            width: "120",
            prop: "restTimeStr",
            label: "预估续航时间",
            width: "80",
        },
    ];
   const testType = ref('');
  const hrTypeList = computed(() => {
    return Object.keys(hrTypes).map(v => {
      return {
        value: v,
        label: hrTypes[v],
      };
    });
  });
    const background = ref(true);
    const disabled = ref(false);
    const pageCurr = ref(1);
@@ -155,30 +120,34 @@
    const dialogTitle = ref("");
    const currentAreaId = ref();
    const currentAreaIds = ref([]);
  const testStartDate = ref('');
  const testEndDate = ref('');
    const datas = reactive({
        tableData: [],
        rowData: {},
    });
    // const tableData = reactive([]);
    // const rowData = reactive({});
    // const userStore = useUserStore();
    // const { uid, uname } = storeToRefs(userStore);
  // 计算属性生成 picker-options(更简洁)
  const startDisabledDate = (time) =>  testEndDate.value ? moment(testEndDate.value).isBefore(time) || moment().isBefore(time) : moment().isBefore(time);
  const endDisabledDate = (time) => testStartDate.value ? moment(time).isBefore(testStartDate.value) || moment().isBefore(time) : moment().isBefore(time);
    function getList() {
        let loading = $loading();
    let params = {
      // city: "",
      // country: "",
      provice: provice.value || undefined,
      city: city.value || undefined,
      country: country.value || undefined,
      stationName: stationName.value || undefined,
      testType: testType.value || undefined,
      testStartTime: testStartDate.value ? testStartDate.value + ' 00:00:00' : undefined,
      testEndTime: testEndDate.value ? testEndDate.value + ' 23:59:59' : undefined,
      pageNum: pageCurr.value,
      pageSize: pageSize.value,
      // powerName: "",
      // provice: "",
      // stationName: "",
    };
        getDevList(params)
        getBattTinfStatistic(params)
            .then((res) => {
                let { code, data, data2 } = res;
                let list = [];
@@ -187,8 +156,9 @@
                    // console.log(data);
                    list = data2.list.map(v => ({
            ...v,
            powerTypeStr: powerTypes[v.powerType],
            // roleName: roles[v.role],
            testTimelongStr: formatSeconds(v.testTimelong),
            restTimeStr: formatSeconds(v.restTime),
            precentCapStr: toFixed(v.precentCap, digits.PREC) + '%',
          }));
                    _total = data2.total;
                }
@@ -214,61 +184,39 @@
        pageCurr.value = val;
        getList();
    }
    function add() {
        dialogTitle.value = "添加设备";
        datas.rowData = {};
        addEditVisible.value = true;
    }
    function edit(record) {
        dialogTitle.value = "编辑设备";
        addEditVisible.value = true;
        console.log(record, '=======edit======');
        // if (record.ainfList.idPath) {
        //     ids = record.ainfList.idPath.split("_").map((v) => v * 1);
        // }
        // ids.push(record.areaId);
        datas.rowData = { ...record };
    }
  function addBatt(record) {
    dialogTitle.value = "添加电池组";
        datas.rowData = { ...record, addBattFlag: true };
        addEditVisible.value = true;
  function filterChange() {
    nextTick(() => {
      pageCurr.value = 1;
      getList();
    });
  }
    function confirmRemove(record) {
        $confirm("删除", () => {
      let { stationId, powerId, battgroupId } = record;
            remove(stationId, powerId, battgroupId||undefined);
        });
    }
    function remove(stationId, powerId, battgroupId) {
        let loading = $loading();
        delBatt(stationId, powerId, battgroupId)
            .then((res) => {
                let { code, data } = res;
                loading.close();
                if (code && data) {
                    $message.success("操作成功");
                    handleCurrentChange(1);
                } else {
                    $message.success("操作失败");
                }
            })
            .catch((err) => {
                loading.close();
                console.log(err);
            });
    }
    function onOk() {
        addEditVisible.value = false;
        handleCurrentChange(1);
    }
    function onCanel() {
        addEditVisible.value = false;
    }
  function goRt (row) {
    router.push({
      path: '/datas/realtime',
      query: {
        stationId: row.stationId || undefined,
        powerId: row.powerId || undefined,
        devId: row.devId || undefined,
        battgroupId: row.battgroupId || undefined,
        pageFlag: Math.random(),
      }
    });
  }
  function goHis (row) {
    router.push({
      path: '/datas/realtime',
      query: {
        id: row.battgroupId
      }
    });
  }
  function exportExcel() {
    ExportFile(headers, datas.tableData, "蓄电池核容信息统计");
  }
    onMounted(() => {
        getList();
@@ -283,102 +231,130 @@
      <yc-card is-full>
        <div class="page-content-wrapper">
          <div class="page-content-tools page-filter">
            <div class="table-row">
              <div class="table-cell text-right">省:</div>
              <div class="table-cell">
                <el-select
                  v-model="provice"
                  size="small"
                  placeholder="请选择省"
                >
                  <el-option
                    v-for="item in proviceList"
                    :key="'l0_' + item"
                    :label="item"
                    :value="item"
            <div class="grid-container" :style="{'--counter': 8}">
              <div class="grid-item">
                <div class="label">省</div>
                <div class="value">
                  <el-select
                    v-model="provice"
                    size="small"
                    clearable
                    @change="filterChange"
                    placeholder="请选择省"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in proviceList"
                      :key="'l0_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="table-cell text-right">市:</div>
              <div class="table-cell">
                <el-select
                  v-model="city"
                  size="small"
                  placeholder="请选择市"
                >
                  <el-option
                    v-for="item in cityList"
                    :key="'l1_' + item"
                    :label="item"
                    :value="item"
              <div class="grid-item">
                <div class="label">市</div>
                <div class="value">
                  <el-select
                    v-model="city"
                    size="small"
                    clearable
                    @change="filterChange"
                    placeholder="请选择市"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in cityList"
                      :key="'l1_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="table-cell text-right">区县:</div>
              <div class="table-cell">
                <el-select
                  v-model="country"
                  size="small"
                  placeholder="请选择区县"
                >
                  <el-option
                    v-for="item in countryList"
                    :key="'l2_' + item"
                    :label="item"
                    :value="item"
              <div class="grid-item">
                <div class="label">区县</div>
                <div class="value">
                  <el-select
                    v-model="country"
                    clearable
                    @change="filterChange"
                    size="small"
                    placeholder="请选择区县"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in countryList"
                      :key="'l2_' + item"
                      :label="item"
                      :value="item"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="table-cell text-right">站点:</div>
              <div class="table-cell">
                <el-select
                  v-model="stationName"
                  size="small"
                  placeholder="请选择站点"
                >
                  <el-option
                    v-for="item in stationList"
                    :key="'l3_' + item.stationId"
                    :label="item.stationName"
                    :value="item.stationName"
              <div class="grid-item">
                <div class="label">站点</div>
                <div class="value">
                  <el-select
                    v-model="stationName"
                    clearable
                    @change="filterChange"
                    size="small"
                    placeholder="请选择站点"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="item in stationList"
                      :key="'l3_' + item.stationId"
                      :label="item.stationName"
                      :value="item.stationName"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="table-cell text-right">品牌:</div>
              <div class="table-cell">
                <el-select
                  v-model="country"
                  size="small"
                  placeholder="请选择品牌"
                >
                  <el-option
                    v-for="item in countryList"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
              <div class="grid-item">
                <div class="label">测试类型</div>
                <div class="value">
                  <el-select
                    v-model="testType"
                    size="small"
                    clearable
                    @change="filterChange"
                    placeholder="请选择"
                  >
                  </el-option>
                </el-select>
                    <el-option
                      v-for="(item, idx) in hrTypeList"
                      :key="'list5_' + idx"
                      :label="item.label"
                      :value="item.value"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="table-cell text-right">电压等级:</div>
              <div class="table-cell">
                <el-select
                  v-model="country"
                  size="small"
                  placeholder="请选择电压等级"
                >
                  <el-option
                    v-for="item in countryList"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                  >
                  </el-option>
                </el-select>
              <div class="grid-item">
                <div class="label">测试开始时间</div>
                <div class="value" style="grid-column: span 5;">
                  <el-date-picker
                    v-model="testStartDate"
                    type="date"
                    size="small"
                    placeholder="选择日期"
                    format="YYYY-MM-DD"
                    value-format="YYYY-MM-DD"
                    :disabled-date="startDisabledDate"
                  />
                  -
                  <el-date-picker
                    v-model="testEndDate"
                    size="small"
                    type="date"
                    placeholder="选择日期"
                    format="YYYY-MM-DD"
                    value-format="YYYY-MM-DD"
                    :disabled-date="endDisabledDate"
                  />
                </div>
              </div>
            </div>
          </div>
@@ -389,16 +365,12 @@
                  <el-table-column type="index" fixed="left" label="序号" width="60"></el-table-column>
                  <el-table-column v-for="header in headers" :key="header.prop" :prop="header.prop" :label="header.label"
                    :min-width="header.width" align="center">
                    <template #default="scope">{{ scope.row[header.prop] || '--' }}</template>
                    <template #default="scope">{{ scope.row[header.prop] }}</template>
                  </el-table-column>
                  <el-table-column label="操作" fixed="right" width="240" align="center">
                    <template #default="scope">
                      <el-button type="primary" size="small"
                        @click="edit(scope.row)">编辑</el-button>
                      <el-button type="danger" size="small"
                        @click="confirmRemove(scope.row)">删除</el-button>
                      <el-button type="primary" size="small"
                        @click="addBatt(scope.row)">添加电池组</el-button>
                      <el-button type="warning" size="small" @click="goRt(scope.row)">实时监测</el-button>
                      <el-button type="primary" size="small" @click="goHis(scope.row)">历史数据</el-button>
                    </template>
                  </el-table-column>
                </el-table>
@@ -407,7 +379,6 @@
          </div>
          <div class="page-content-page">
            <div class="page-tool">
              <el-button type="primary" round size="small" @click="add" :icon="Plus">添加</el-button>
              <el-button type="primary" round size="small" @click="getList" :icon="Search">查询</el-button>
            </div>
            <div class="el-page-container">
@@ -416,17 +387,14 @@
                :background="background" layout="total, sizes, prev, pager, next, jumper" :total="total"
                @size-change="handleSizeChange" @current-change="handleCurrentChange" />
            </div>
            <div class="page-tool"></div>
            <div class="page-tool">
              <el-button type="primary" round size="small" @click="exportExcel">导出</el-button>
            </div>
          </div>
        </div>
      </yc-card>
    </div>
    <div class="page-footer"></div>
    <!-- 弹窗 -->
    <el-dialog :title="dialogTitle" v-model="addEditVisible" top="0" :close-on-click-modal="false" class="dialog-center"
      width="860px" center>
      <add-edit v-if="addEditVisible" @success="onOk" :info="datas.rowData" @cancel="onCanel"></add-edit>
    </el-dialog>
  </div>
</template>
@@ -513,4 +481,7 @@
    }
  }
}
.max-width {
  max-width: 200px;
}
</style>