Files
emsapp/pages/index.vue
2026-02-08 17:26:51 +08:00

404 lines
9.2 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>
<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>