2025-09-30 10:01:16 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="trends-container">
|
|
|
|
|
|
<!-- 月度能耗图表区域 -->
|
|
|
|
|
|
<el-row class="chart-group">
|
|
|
|
|
|
<el-col :span="24">
|
|
|
|
|
|
<div class="chart-header">
|
|
|
|
|
|
<h3>月度能耗趋势</h3>
|
|
|
|
|
|
<div class="chart-type-btn-group">
|
|
|
|
|
|
<el-button :type="monthChartType === 'bar' ? 'primary' : 'default'" @click="toggleMonthChartType('bar')"
|
|
|
|
|
|
:disabled="!hasMonthData">
|
|
|
|
|
|
柱状图
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
<el-button :type="monthChartType === 'line' ? 'primary' : 'default'" @click="toggleMonthChartType('line')"
|
|
|
|
|
|
:disabled="!hasMonthData">
|
|
|
|
|
|
折线图
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="chart-wrapper">
|
|
|
|
|
|
<div ref="monthChart" class="chart-container"></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 日度能耗图表区域 -->
|
|
|
|
|
|
<el-row class="chart-group">
|
|
|
|
|
|
<el-col :span="24">
|
|
|
|
|
|
<div class="chart-header">
|
|
|
|
|
|
<h3>日度能耗趋势</h3>
|
|
|
|
|
|
<div class="chart-type-btn-group">
|
|
|
|
|
|
<el-button :type="dayChartType === 'bar' ? 'primary' : 'default'" @click="toggleDayChartType('bar')"
|
|
|
|
|
|
:disabled="!hasDayData">
|
|
|
|
|
|
柱状图
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
<el-button :type="dayChartType === 'line' ? 'primary' : 'default'" @click="toggleDayChartType('line')"
|
|
|
|
|
|
:disabled="!hasDayData">
|
|
|
|
|
|
折线图
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="chart-wrapper">
|
|
|
|
|
|
<div ref="dayChart" class="chart-container"></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
import * as echarts from 'echarts'
|
|
|
|
|
|
import { getRecentEnergySummary } from '@/api/ems/dashboard/timer'
|
|
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
|
name: 'RecentTrends',
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
monthData: [], // 原始月度数据
|
|
|
|
|
|
dayData: [], // 原始日度数据
|
|
|
|
|
|
completedMonthData: [], // 补全后的月度数据
|
|
|
|
|
|
completedDayData: [], // 补全后的日度数据
|
|
|
|
|
|
monthChartType: 'bar',
|
|
|
|
|
|
dayChartType: 'bar',
|
|
|
|
|
|
monthChart: null,
|
|
|
|
|
|
dayChart: null,
|
|
|
|
|
|
hasMonthData: false, // 标记是否有月度数据
|
|
|
|
|
|
hasDayData: false // 标记是否有日度数据
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
props: {
|
|
|
|
|
|
unit: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
required: true
|
|
|
|
|
|
},
|
|
|
|
|
|
energyName: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
required: true
|
|
|
|
|
|
},
|
|
|
|
|
|
energyType: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
required: true
|
|
|
|
|
|
},
|
|
|
|
|
|
locationId: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
required: false
|
|
|
|
|
|
},
|
|
|
|
|
|
deviceId: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
required: false
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
mounted() {
|
|
|
|
|
|
const dayChartDOM = this.$refs.dayChart
|
|
|
|
|
|
console.log(dayChartDOM, this.$refs)
|
|
|
|
|
|
if (!dayChartDOM) return
|
|
|
|
|
|
this.dayChart = echarts.init(dayChartDOM)
|
|
|
|
|
|
|
|
|
|
|
|
const monthChartDOM = this.$refs.monthChart
|
|
|
|
|
|
if (!monthChartDOM) return
|
|
|
|
|
|
this.monthChart = echarts.init(monthChartDOM)
|
|
|
|
|
|
this.fetchMonthData()
|
|
|
|
|
|
this.fetchDayData()
|
|
|
|
|
|
window.addEventListener('resize', this.handleWindowResize)
|
|
|
|
|
|
},
|
|
|
|
|
|
beforeDestroy() {
|
|
|
|
|
|
this.destroyCharts()
|
|
|
|
|
|
window.removeEventListener('resize', this.handleWindowResize)
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
// 销毁图表实例
|
|
|
|
|
|
destroyCharts() {
|
|
|
|
|
|
if (this.monthChart) {
|
|
|
|
|
|
this.monthChart.dispose()
|
|
|
|
|
|
this.monthChart = null
|
|
|
|
|
|
}
|
|
|
|
|
|
if (this.dayChart) {
|
|
|
|
|
|
this.dayChart.dispose()
|
|
|
|
|
|
this.dayChart = null
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 数据请求
|
|
|
|
|
|
fetchMonthData() {
|
|
|
|
|
|
const year = new Date().getFullYear()
|
|
|
|
|
|
getRecentEnergySummary({ year, energyType: this.energyType, locationId: this.locationId, deviceId: this.deviceId })
|
|
|
|
|
|
.then(res => {
|
|
|
|
|
|
this.monthData = res.data || []
|
|
|
|
|
|
console.log(this.monthData)
|
|
|
|
|
|
// 检查是否有有效数据
|
|
|
|
|
|
this.hasMonthData = Array.isArray(this.monthData) && this.monthData.length > 0
|
|
|
|
|
|
|
|
|
|
|
|
this.completedMonthData = this.completeMonthData(this.monthData)
|
|
|
|
|
|
this.initMonthChart()
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
fetchDayData() {
|
|
|
|
|
|
const date = new Date()
|
|
|
|
|
|
const month = date.getFullYear() + '-' + (date.getMonth() + 1).toString().padStart(2, '0')
|
|
|
|
|
|
getRecentEnergySummary({ month, energyType: this.energyType, locationId: this.locationId, deviceId: this.deviceId })
|
|
|
|
|
|
.then(res => {
|
|
|
|
|
|
this.dayData = res.data || []
|
|
|
|
|
|
console.log(this.dayData)
|
|
|
|
|
|
|
|
|
|
|
|
this.completedDayData = this.completeDayData(this.dayData)
|
|
|
|
|
|
this.initDayChart()
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 补全月度数据 - 固定显示12个月份(一整年)
|
|
|
|
|
|
completeMonthData(data) {
|
|
|
|
|
|
this.hasMonthData = Array.isArray(this.monthData) && this.monthData.length > 0
|
|
|
|
|
|
if (!this.hasMonthData) {
|
|
|
|
|
|
const result = []
|
|
|
|
|
|
for (let i = 0; i < 12; i++) {
|
|
|
|
|
|
result.push({ month: i + 1, totalConsumption: 0 })
|
|
|
|
|
|
}
|
|
|
|
|
|
return result
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const result = []
|
|
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < 12; i++) {
|
|
|
|
|
|
// 查找data中month{year}-{month}中{month}为i+1的数据
|
|
|
|
|
|
const item = data.find(item => item.month?.split('-')[1].padStart(2, '0') == i + 1)
|
|
|
|
|
|
console.log(item, i + 1, data)
|
|
|
|
|
|
if (item) {
|
|
|
|
|
|
result.push(item)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
result.push({ month: i + 1, totalConsumption: 0 })
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 补全日度数据 - 按照当月实际天数补齐
|
|
|
|
|
|
completeDayData(data) {
|
|
|
|
|
|
this.hasDayData = Array.isArray(this.dayData) && this.dayData.length > 0
|
|
|
|
|
|
|
|
|
|
|
|
// 本月天数
|
|
|
|
|
|
const today = new Date()
|
|
|
|
|
|
const dayCount = new Date(today.getFullYear(), today.getMonth() + 1, 0).getDate()
|
|
|
|
|
|
if (!this.hasDayData) {
|
|
|
|
|
|
const result = []
|
|
|
|
|
|
for (let i = 0; i < dayCount; i++) {
|
|
|
|
|
|
result.push({ day: i + 1, totalConsumption: 0 })
|
|
|
|
|
|
}
|
|
|
|
|
|
return result
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 补全数据
|
|
|
|
|
|
const result = []
|
|
|
|
|
|
for (let i = 0; i < dayCount; i++) {
|
|
|
|
|
|
const item = data.find(item => item.day?.split('-')[2].padStart(2, '0') == i + 1)
|
|
|
|
|
|
if (item) {
|
|
|
|
|
|
result.push(item)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
result.push({ day: i + 1, totalConsumption: 0 })
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化月度图表
|
|
|
|
|
|
initMonthChart() {
|
|
|
|
|
|
if (!this.hasMonthData) return
|
|
|
|
|
|
|
|
|
|
|
|
console.log(this.completedMonthData)
|
|
|
|
|
|
const xData = this.completedMonthData.map(item => item.month)
|
|
|
|
|
|
const yData = this.completedMonthData.map(item => item.totalConsumption)
|
|
|
|
|
|
|
|
|
|
|
|
const option = {
|
|
|
|
|
|
tooltip: {
|
|
|
|
|
|
trigger: 'axis',
|
|
|
|
|
|
formatter: '{b}: {c} ' + this.unit
|
|
|
|
|
|
},
|
|
|
|
|
|
grid: {
|
|
|
|
|
|
left: '3%',
|
|
|
|
|
|
right: '4%',
|
|
|
|
|
|
bottom: '3%',
|
|
|
|
|
|
containLabel: true
|
|
|
|
|
|
},
|
|
|
|
|
|
xAxis: {
|
|
|
|
|
|
type: 'category',
|
|
|
|
|
|
data: xData,
|
|
|
|
|
|
axisLabel: {
|
|
|
|
|
|
rotate: 15
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
yAxis: {
|
|
|
|
|
|
type: 'value',
|
|
|
|
|
|
name: '能耗 (' + this.unit + ')',
|
|
|
|
|
|
min: 0
|
|
|
|
|
|
},
|
|
|
|
|
|
series: [
|
|
|
|
|
|
{
|
|
|
|
|
|
name: '总能耗',
|
|
|
|
|
|
type: this.monthChartType,
|
|
|
|
|
|
data: yData,
|
|
|
|
|
|
itemStyle: {
|
|
|
|
|
|
color: this.monthChartType === 'bar' ? '#409EFF' : '#67C23A'
|
|
|
|
|
|
},
|
|
|
|
|
|
markPoint: {
|
|
|
|
|
|
data: [
|
|
|
|
|
|
{ type: 'max', name: '最大值', itemStyle: { color: '#F56C6C' } },
|
|
|
|
|
|
{ type: 'min', name: '最小值', itemStyle: { color: '#909399' } }
|
|
|
|
|
|
],
|
|
|
|
|
|
label: {
|
|
|
|
|
|
formatter: '{b}: {c} ' + this.unit
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
smooth: this.monthChartType === 'line'
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
console.log(option)
|
|
|
|
|
|
this.monthChart.setOption(option)
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化日度图表
|
|
|
|
|
|
initDayChart() {
|
|
|
|
|
|
if (!this.hasDayData) return
|
|
|
|
|
|
|
|
|
|
|
|
const xData = this.completedDayData.map(item => item.day)
|
|
|
|
|
|
const yData = this.completedDayData.map(item => item.totalConsumption)
|
|
|
|
|
|
|
|
|
|
|
|
const option = {
|
|
|
|
|
|
tooltip: {
|
|
|
|
|
|
trigger: 'axis',
|
|
|
|
|
|
formatter: '{b}: {c} ' + this.unit
|
|
|
|
|
|
},
|
|
|
|
|
|
grid: {
|
|
|
|
|
|
left: '3%',
|
|
|
|
|
|
right: '4%',
|
|
|
|
|
|
bottom: '3%',
|
|
|
|
|
|
containLabel: true
|
|
|
|
|
|
},
|
|
|
|
|
|
xAxis: {
|
|
|
|
|
|
type: 'category',
|
|
|
|
|
|
data: xData,
|
|
|
|
|
|
axisLabel: {
|
|
|
|
|
|
rotate: 15
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
yAxis: {
|
|
|
|
|
|
type: 'value',
|
|
|
|
|
|
name: '能耗 (' + this.unit + ')',
|
|
|
|
|
|
min: 0
|
|
|
|
|
|
},
|
|
|
|
|
|
series: [
|
|
|
|
|
|
{
|
|
|
|
|
|
name: '总能耗',
|
|
|
|
|
|
type: this.dayChartType,
|
|
|
|
|
|
data: yData,
|
|
|
|
|
|
itemStyle: {
|
|
|
|
|
|
color: this.dayChartType === 'bar' ? '#409EFF' : '#67C23A'
|
|
|
|
|
|
},
|
|
|
|
|
|
markPoint: {
|
|
|
|
|
|
data: [
|
|
|
|
|
|
{ type: 'max', name: '最大值', itemStyle: { color: '#F56C6C' } },
|
|
|
|
|
|
{ type: 'min', name: '最小值', itemStyle: { color: '#909399' } }
|
|
|
|
|
|
],
|
|
|
|
|
|
label: {
|
|
|
|
|
|
formatter: '{b}: {c} ' + this.unit
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
smooth: this.dayChartType === 'line'
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.dayChart.setOption(option)
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 切换图表类型
|
|
|
|
|
|
toggleMonthChartType(type) {
|
|
|
|
|
|
this.monthChartType = type
|
|
|
|
|
|
this.initMonthChart()
|
|
|
|
|
|
},
|
|
|
|
|
|
toggleDayChartType(type) {
|
|
|
|
|
|
this.dayChartType = type
|
|
|
|
|
|
this.initDayChart()
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 窗口大小调整
|
|
|
|
|
|
handleWindowResize() {
|
|
|
|
|
|
if (this.monthChart) this.monthChart.resize()
|
|
|
|
|
|
if (this.dayChart) this.dayChart.resize()
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 对外暴露的刷新方法
|
|
|
|
|
|
refresh() {
|
|
|
|
|
|
this.fetchMonthData()
|
|
|
|
|
|
this.fetchDayData()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.trends-container {
|
|
|
|
|
|
padding: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.chart-group {
|
|
|
|
|
|
margin-bottom: 30px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.chart-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.chart-type-btn-group button {
|
|
|
|
|
|
margin-left: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.chart-wrapper {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 300px;
|
|
|
|
|
|
border: 1px solid #eee;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.chart-container {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
padding: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.no-data {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|