Files
klp-oa/klp-ui/src/views/ems/dashboard/panels/RecentTrends.vue
2025-09-30 10:01:16 +08:00

383 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>