2026-02-08 17:26:51 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<view class="home-container">
|
|
|
|
|
|
<view class="site-sections-list">
|
|
|
|
|
|
<view class="site-title">站点选择</view>
|
|
|
|
|
|
<checkbox-group class="site-radio" @change="onSiteChange">
|
|
|
|
|
|
<label v-for="item in siteOptions" :key="item.value" class="radio-item"
|
|
|
|
|
|
:class="{ active: selectedSiteIds.includes(item.value), disabled: item.disable }">
|
|
|
|
|
|
<checkbox class="radio" :value="item.value" :disabled="item.disable"
|
|
|
|
|
|
:checked="selectedSiteIds.includes(item.value)" color="#547ef4" />
|
|
|
|
|
|
<text class="radio-text">{{ item.text }}</text>
|
|
|
|
|
|
</label>
|
|
|
|
|
|
</checkbox-group>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="base-info">
|
|
|
|
|
|
<view class="total-card">
|
|
|
|
|
|
<view class="total-header">
|
|
|
|
|
|
<view class="title">总累计运行数据</view>
|
|
|
|
|
|
<view class="total-revenue">
|
|
|
|
|
|
<text class="label">总收入</text>
|
|
|
|
|
|
<text class="value">{{ format2(runningInfo.totalRevenue) }}</text>
|
|
|
|
|
|
<text class="unit">元</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<uni-grid :column="2" :showBorder="false" :square="false" :highlight="false">
|
|
|
|
|
|
<uni-grid-item v-for="(item, index) in sjglData" :key="index + 'sjglData'">
|
|
|
|
|
|
<view class="grid-item-box">
|
|
|
|
|
|
<view class="title">{{ item.title }}</view>
|
|
|
|
|
|
<view class="text" :style="{ color: item.color }">
|
|
|
|
|
|
{{ format2(runningInfo[item.attr]) }}
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</uni-grid-item>
|
|
|
|
|
|
</uni-grid>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<uni-section title="收入曲线" type="line" class="sections-list">
|
|
|
|
|
|
<view style="width:100%;height: 220px;">
|
|
|
|
|
|
<qiun-data-charts type="line" :chartData="revenueChartData" :optsWatch='false'
|
|
|
|
|
|
:inScrollView="true" :pageScrollTop="pageScrollTop" :opts="revenueOptions" :ontouch="true" />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</uni-section>
|
|
|
|
|
|
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
import {
|
|
|
|
|
|
mapGetters
|
|
|
|
|
|
} from 'vuex'
|
|
|
|
|
|
import {
|
|
|
|
|
|
formatDate
|
|
|
|
|
|
} from '@/utils/filters'
|
|
|
|
|
|
import {
|
|
|
|
|
|
getAllSites,
|
|
|
|
|
|
getDzjkHomeView,
|
|
|
|
|
|
getAmmeterRevenueData
|
|
|
|
|
|
} from '@/api/ems/site.js'
|
|
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
pageScrollTop: 0,
|
|
|
|
|
|
siteOptions: [],
|
|
|
|
|
|
selectedSiteIds: [],
|
|
|
|
|
|
runningInfo: {},
|
|
|
|
|
|
revenueChartData: {},
|
|
|
|
|
|
revenueOptions: {
|
|
|
|
|
|
padding: [10, 5, 0, 10],
|
|
|
|
|
|
dataLabel: false,
|
|
|
|
|
|
enableScroll: false,
|
|
|
|
|
|
xAxis: {
|
|
|
|
|
|
scrollShow: false,
|
|
|
|
|
|
itemCount: 5,
|
|
|
|
|
|
disableGrid: true
|
|
|
|
|
|
},
|
|
|
|
|
|
yAxis: {
|
|
|
|
|
|
disabled: false,
|
|
|
|
|
|
splitNumber: 4
|
|
|
|
|
|
},
|
|
|
|
|
|
extra: {
|
|
|
|
|
|
line: {
|
|
|
|
|
|
type: "curve",
|
|
|
|
|
|
width: 2
|
|
|
|
|
|
},
|
|
|
|
|
|
area: {
|
|
|
|
|
|
type: "curve",
|
|
|
|
|
|
opacity: 0.2,
|
|
|
|
|
|
addLine: true,
|
|
|
|
|
|
gradient: true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
sjglData: [{
|
|
|
|
|
|
title: "今日充电量(kWh)",
|
|
|
|
|
|
attr: "dayChargedCap",
|
|
|
|
|
|
color: '#4472c4'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "今日放电量(kWh)",
|
|
|
|
|
|
attr: "dayDisChargedCap",
|
|
|
|
|
|
color: '#70ad47'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "总充电量(kWh)",
|
|
|
|
|
|
attr: "totalChargedCap",
|
|
|
|
|
|
color: '#4472c4'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "今日实时收入(元)",
|
|
|
|
|
|
attr: "dayRevenue",
|
|
|
|
|
|
color: '#f67438'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "昨日充电量(kWh)",
|
|
|
|
|
|
attr: "yesterdayChargedCap",
|
|
|
|
|
|
color: '#4472c4'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "昨日放电量(kWh)",
|
|
|
|
|
|
attr: "yesterdayDisChargedCap",
|
|
|
|
|
|
color: '#70ad47'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "总放电量(kWh)",
|
|
|
|
|
|
attr: "totalDischargedCap",
|
|
|
|
|
|
color: '#70ad47'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "昨日实时收入(元)",
|
|
|
|
|
|
attr: "yesterdayRevenue",
|
|
|
|
|
|
color: '#f67438'
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
computed: {
|
|
|
|
|
|
...mapGetters(['belongSite'])
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
format2(value) {
|
|
|
|
|
|
const num = Number(value || 0)
|
|
|
|
|
|
return Number.isFinite(num) ? num.toFixed(2) : '0.00'
|
|
|
|
|
|
},
|
|
|
|
|
|
getLastDaysRange(days = 7) {
|
|
|
|
|
|
const end = new Date()
|
|
|
|
|
|
const start = new Date(end.getTime() - (days - 1) * 24 * 60 * 60 * 1000)
|
|
|
|
|
|
return [formatDate(start), formatDate(end)]
|
|
|
|
|
|
},
|
|
|
|
|
|
buildDateList(start, end) {
|
|
|
|
|
|
const list = []
|
|
|
|
|
|
const startTime = new Date(start).getTime()
|
|
|
|
|
|
const endTime = new Date(end).getTime()
|
|
|
|
|
|
const dayMs = 24 * 60 * 60 * 1000
|
|
|
|
|
|
for (let t = startTime; t <= endTime; t += dayMs) {
|
|
|
|
|
|
list.push(formatDate(t))
|
|
|
|
|
|
}
|
|
|
|
|
|
return list
|
|
|
|
|
|
},
|
|
|
|
|
|
onSiteChange(e) {
|
|
|
|
|
|
const values = e.detail.value || []
|
|
|
|
|
|
this.selectedSiteIds = values
|
|
|
|
|
|
this.updateSiteInfo()
|
|
|
|
|
|
},
|
|
|
|
|
|
updateSiteInfo() {
|
|
|
|
|
|
if (!this.selectedSiteIds || this.selectedSiteIds.length === 0) {
|
|
|
|
|
|
this.runningInfo = {}
|
|
|
|
|
|
this.revenueChartData = {}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
this.getRunningInfo()
|
|
|
|
|
|
this.getRevenueChartData()
|
|
|
|
|
|
},
|
|
|
|
|
|
getSiteList() {
|
|
|
|
|
|
getAllSites().then(response => {
|
|
|
|
|
|
const data = response?.data || []
|
|
|
|
|
|
this.siteOptions = data.map(item => {
|
|
|
|
|
|
return {
|
|
|
|
|
|
text: item.siteName,
|
|
|
|
|
|
value: item.siteId,
|
|
|
|
|
|
disable: !this.belongSite || this.belongSite.length === 0 || this.belongSite.includes('all') ? false : !this
|
|
|
|
|
|
.belongSite.includes(item.siteId)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
const defaultSite = this.siteOptions.find(item => !item.disable)?.value || ''
|
|
|
|
|
|
this.selectedSiteIds = defaultSite ? [defaultSite] : []
|
|
|
|
|
|
this.selectedSiteIds.length > 0 && this.updateSiteInfo()
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
getRunningInfo() {
|
|
|
|
|
|
const sumFields = ['totalRevenue', ...this.sjglData.map(item => item.attr)]
|
|
|
|
|
|
const requests = this.selectedSiteIds.map(siteId => getDzjkHomeView({
|
|
|
|
|
|
siteId
|
|
|
|
|
|
}).then(response => response?.data || {}))
|
|
|
|
|
|
Promise.all(requests).then(list => {
|
|
|
|
|
|
const result = {}
|
|
|
|
|
|
sumFields.forEach(key => {
|
|
|
|
|
|
result[key] = list.reduce((sum, item) => {
|
|
|
|
|
|
const value = Number(item?.[key] || 0)
|
|
|
|
|
|
return sum + (Number.isFinite(value) ? value : 0)
|
|
|
|
|
|
}, 0)
|
|
|
|
|
|
})
|
|
|
|
|
|
this.runningInfo = result
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
getRevenueChartData() {
|
|
|
|
|
|
const [startTime, endTime] = this.getLastDaysRange(7)
|
|
|
|
|
|
const dateList = this.buildDateList(startTime, endTime)
|
|
|
|
|
|
const requests = this.selectedSiteIds.map(siteId => getAmmeterRevenueData({
|
|
|
|
|
|
siteId,
|
|
|
|
|
|
startTime,
|
|
|
|
|
|
endTime,
|
|
|
|
|
|
pageSize: 200,
|
|
|
|
|
|
pageNum: 1
|
|
|
|
|
|
}).then(response => ({
|
|
|
|
|
|
siteId,
|
|
|
|
|
|
rows: response?.rows || []
|
|
|
|
|
|
})))
|
|
|
|
|
|
Promise.all(requests).then(list => {
|
|
|
|
|
|
const categories = dateList.map(day => day.slice(5))
|
|
|
|
|
|
const series = list.map(item => {
|
|
|
|
|
|
const sumMap = {}
|
|
|
|
|
|
dateList.forEach(day => {
|
|
|
|
|
|
sumMap[day] = 0
|
|
|
|
|
|
})
|
|
|
|
|
|
item.rows.forEach(row => {
|
|
|
|
|
|
const day = row.dataTime || row.statisDate || row.date
|
|
|
|
|
|
if (!day) return
|
|
|
|
|
|
if (!Object.prototype.hasOwnProperty.call(sumMap, day)) {
|
|
|
|
|
|
sumMap[day] = 0
|
|
|
|
|
|
}
|
|
|
|
|
|
const value = Number(row.actualRevenue || row.revenue || 0)
|
|
|
|
|
|
sumMap[day] += Number.isFinite(value) ? value : 0
|
|
|
|
|
|
})
|
|
|
|
|
|
const name = this.siteOptions.find(opt => opt.value === item.siteId)?.text || item.siteId
|
|
|
|
|
|
return {
|
|
|
|
|
|
name,
|
|
|
|
|
|
data: dateList.map(day => Number((sumMap[day] || 0).toFixed(2)))
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
this.revenueChartData = JSON.parse(JSON.stringify({
|
|
|
|
|
|
categories,
|
|
|
|
|
|
series
|
|
|
|
|
|
}))
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
onLoad() {
|
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
|
this.getSiteList()
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
onPageScroll(e) {
|
|
|
|
|
|
this.pageScrollTop = e.scrollTop
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
/* #ifndef APP-NVUE */
|
|
|
|
|
|
page {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
min-height: 100%;
|
|
|
|
|
|
height: auto;
|
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
|
line-height: 30rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
view {
|
|
|
|
|
|
line-height: inherit;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* #endif */
|
|
|
|
|
|
|
|
|
|
|
|
.home-container {
|
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.site-sections-list {
|
|
|
|
|
|
background: linear-gradient(to right, #547ef4, #679ff5);
|
|
|
|
|
|
padding: 30rpx 30rpx 100rpx;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
|
|
|
|
|
|
.site-title {
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
margin-bottom: 16rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.site-radio {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 16rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.radio-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 12rpx 20rpx;
|
|
|
|
|
|
border-radius: 28rpx;
|
|
|
|
|
|
background: rgba(0, 0, 0, 0.18);
|
|
|
|
|
|
color: rgba(255, 255, 255, 0.75);
|
|
|
|
|
|
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
|
|
|
|
|
|
transition: all 0.2s ease;
|
|
|
|
|
|
|
|
|
|
|
|
&.active {
|
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
|
color: #233157;
|
|
|
|
|
|
box-shadow: 0 12rpx 22rpx rgba(0, 0, 0, 0.18);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&.disabled {
|
|
|
|
|
|
opacity: 0.45;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.radio {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.radio-text {
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
letter-spacing: 0.5rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.base-info {
|
|
|
|
|
|
margin-top: -80rpx;
|
|
|
|
|
|
border-radius: 80rpx 80rpx 0 0;
|
|
|
|
|
|
padding: 30rpx;
|
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
|
|
|
|
|
|
.total-card {
|
|
|
|
|
|
margin-top: 30rpx;
|
|
|
|
|
|
border-radius: 16rpx;
|
|
|
|
|
|
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.08);
|
|
|
|
|
|
padding: 20rpx;
|
|
|
|
|
|
|
|
|
|
|
|
.total-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin-bottom: 20rpx;
|
|
|
|
|
|
|
|
|
|
|
|
.title {
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.total-revenue {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: baseline;
|
|
|
|
|
|
gap: 6rpx;
|
|
|
|
|
|
font-size: 22rpx;
|
|
|
|
|
|
color: #19242d;
|
|
|
|
|
|
|
|
|
|
|
|
.value {
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: #f67438;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.grid-item-box {
|
|
|
|
|
|
padding-top: 6rpx;
|
|
|
|
|
|
padding-bottom: 6rpx;
|
|
|
|
|
|
text-align: left;
|
|
|
|
|
|
|
|
|
|
|
|
.title {
|
|
|
|
|
|
font-size: 22rpx;
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.text {
|
|
|
|
|
|
margin-top: 10rpx;
|
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sections-list {
|
|
|
|
|
|
margin-bottom: 10rpx;
|
|
|
|
|
|
|
|
|
|
|
|
::v-deep &>.uni-section-header {
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
|
line-height: 30rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sections-list:not(:first-child) {
|
|
|
|
|
|
margin-top: 40rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|