This commit is contained in:
2026-04-01 14:28:09 +08:00
parent 69e199e9cc
commit ac7dd9dd30
8 changed files with 637 additions and 363 deletions

View File

@ -1,32 +1,20 @@
<template>
<view class="work-container">
<view class="site-sections-list">
<uni-data-picker placeholder="请选择" popup-title="业态选择" :step-searh="true" :value="siteId" :clear-icon="false"
:localdata="siteTypeOptions" :ellipsis="false" @change="selectedSite">
</uni-data-picker>
<view class="info">
<view class="list"> <uni-icons type="location" color="#fff" size="20"></uni-icons>
{{baseInfo.siteAddress || '-'}}
</view>
<view class="list">
<uni-icons type="calendar" color="#fff" size="20"></uni-icons>
{{baseInfo.runningTime || '-'}}
</view>
</view>
</view>
<template>
<view class="work-container">
<site-switch-header :site-id="siteId" :site-type-options="siteTypeOptions" :site-address="baseInfo.siteAddress"
:running-time="baseInfo.runningTime" @change="selectedSite" />
<!-- 静态信息 -->
<view class="base-info">
<uni-group mode="card" class="install-data">
<uni-grid :column="2" :showBorder="false" :square="false" :highlight="false">
<uni-grid-item>
<view class="grid-item-box">
<view class="title">装机功率(MW)</view>
<view class="title">装机功率(MWh)</view>
<view class="text">{{baseInfo.installPower | formatNumber}}</view>
</view>
</uni-grid-item>
<uni-grid-item>
<view class="grid-item-box">
<view class="title">装机容量(MW)</view>
<view class="title">装机容量(MWh)</view>
<view class="text">{{baseInfo.installCapacity | formatNumber}}</view>
</view>
</uni-grid-item>
@ -66,29 +54,49 @@
</template>
<script>
import {
mapGetters
} from 'vuex'
import DateRangeSelect from './DateRangeSelect.vue'
import {
getAllSites,
getSingleSiteBaseInfo,
getSevenChargeData,
getPointData,
getSiteAllDeviceCategory
} from '@/api/ems/site.js'
export default {
components: {
DateRangeSelect
},
import {
mapGetters
} from 'vuex'
import DateRangeSelect from './DateRangeSelect.vue'
import SiteSwitchHeader from '@/components/SiteSwitchHeader/index.vue'
import {
getAllSites,
getSingleSiteBaseInfo,
getProjectDisplayData,
getPointConfigCurve,
getSiteAllDeviceCategory
} from '@/api/ems/site.js'
const createSiteTypeOptions = () => ([{
text: '储能',
value: 'cn',
children: []
},
{
text: '光能',
value: 'gn',
children: []
},
{
text: '岸电',
value: 'ad',
children: []
}
])
export default {
components: {
DateRangeSelect,
SiteSwitchHeader
},
data() {
return {
// 图表数据
weekChartTimeRange: [],
activeChartTimeRange: [],
weekChartData: {},
activeChartData: {},
pageScrollTop: 0,
activeChartData: {},
curveDisplayData: [],
curveDisplayLoadingPromise: null,
pageScrollTop: 0,
glqxOptions: {
padding: [10, 5, 0, 10],
dataLabel: false,
@ -140,25 +148,8 @@
// padding: [10, 15, 10, 15]
},
// 图表数据结束
deviceCategoryOptions: [], //当前站点包含的设备类别
siteTypeOptions: [{
text: '储能',
value: 'cn',
children: []
},
{
text: '光能',
value: 'gn',
disable: true,
children: []
},
{
text: '岸电',
value: 'ad',
disable: true,
children: []
}
],
deviceCategoryOptions: [], //当前站点包含的设备类别
siteTypeOptions: createSiteTypeOptions(),
siteId: '', //选择的站点ID
baseInfo: {}, //站点基本信息
gridList: [{
@ -208,7 +199,10 @@
},
methods: {
isAvailableSite(siteId) {
const site = (this.siteTypeOptions.find(i => i.value === 'cn')?.children || []).find(item => item.value === siteId)
const allSites = this.siteTypeOptions.reduce((result, typeItem) => {
return result.concat(typeItem.children || [])
}, [])
const site = allSites.find(item => item.value === siteId)
return !!(site && !site.disable)
},
// 更新一周冲放曲线时间范围 重置图表
@ -240,30 +234,146 @@
this.$store.commit('SET_CURRENTSITEID', value)
this.updateSiteInfo()
},
updateSiteInfo() {
if (!this.siteId) return
this.getSiteBaseInfo()
this.getWeekChartData()
this.getGVQXData()
this.getSiteDeviceCategory()
},
getSiteList() {
getAllSites().then(response => {
const data = response?.data || []
this.siteTypeOptions.find(i => i.value === 'cn').children = data.map(item => {
updateSiteInfo() {
if (!this.siteId) return
this.curveDisplayData = []
this.curveDisplayLoadingPromise = null
this.getSiteBaseInfo()
this.getWeekChartData()
this.getGVQXData()
this.getSiteDeviceCategory()
},
getFieldName(fieldCode) {
const raw = String(fieldCode || '').trim()
if (!raw) return ''
const index = raw.lastIndexOf('__')
return index >= 0 ? raw.slice(index + 2) : raw
},
normalizeDateTime(value, endOfDay) {
const raw = String(value || '').trim()
if (!raw) return ''
if (raw.includes(' ')) return raw
return `${raw} ${endOfDay ? '23:59:59' : '00:00:00'}`
},
parseToTimestamp(value) {
if (!value) return null
const timestamp = new Date(value).getTime()
return Number.isNaN(timestamp) ? null : timestamp
},
ensureCurveDisplayData() {
if (!this.siteId) return Promise.resolve([])
if (this.curveDisplayData.length > 0) return Promise.resolve(this.curveDisplayData)
if (this.curveDisplayLoadingPromise) return this.curveDisplayLoadingPromise
this.curveDisplayLoadingPromise = getProjectDisplayData(this.siteId)
.then(response => {
this.curveDisplayData = response?.data || []
return this.curveDisplayData
})
.catch(() => {
this.curveDisplayData = []
return []
})
.finally(() => {
this.curveDisplayLoadingPromise = null
})
return this.curveDisplayLoadingPromise
},
fetchCurveSeries(pointId, name, startTime, endTime) {
if (!pointId) return Promise.resolve(null)
return getPointConfigCurve({
siteId: this.siteId,
pointId,
pointType: 'data',
rangeType: 'custom',
startTime,
endTime
}).then(response => {
const list = response?.data || []
const points = list.map(item => {
const timestamp = this.parseToTimestamp(item.dataTime)
const value = Number(item.pointValue)
if (!timestamp || Number.isNaN(value)) return null
return {
text: item.siteName,
value: item.siteId,
id: item.id,
disable: !this.belongSite || this.belongSite.length === 0 || this
.belongSite.includes('all') ? false : !this.belongSite.includes(item
.siteId)
timestamp,
label: item.dataTime,
value
}
}).filter(Boolean)
return {
name,
points
}
}).catch(() => null)
},
buildLineChartData(seriesList) {
const validSeries = (seriesList || []).filter(item => item && (item.points || []).length > 0)
if (validSeries.length === 0) {
return {
categories: [],
series: []
}
}
const labelByTimestamp = {}
const timestampSet = new Set()
validSeries.forEach(item => {
item.points.forEach(point => {
timestampSet.add(point.timestamp)
if (!labelByTimestamp[point.timestamp]) {
labelByTimestamp[point.timestamp] = point.label
}
})
const siteChildren = this.siteTypeOptions.find(i => i.value === 'cn')?.children || []
})
const sortedTimestamps = Array.from(timestampSet).sort((a, b) => a - b)
const categories = sortedTimestamps.map(item => labelByTimestamp[item])
const series = validSeries.map(item => {
const pointMap = {}
item.points.forEach(point => {
pointMap[point.timestamp] = point.value
})
return {
name: item.name,
data: sortedTimestamps.map(timestamp => pointMap[timestamp] ?? null)
}
})
return {
categories,
series
}
},
findActiveCurveRow(sectionRows, keywords = []) {
const keywordSet = new Set((keywords || []).map(item => String(item || '').toLowerCase()))
return (sectionRows || []).find(row => {
const fieldCode = this.getFieldName(row?.fieldCode).toLowerCase()
const fieldName = String(row?.fieldName || '').toLowerCase()
if (keywordSet.has(fieldCode) || keywordSet.has(fieldName)) return true
return Array.from(keywordSet).some(keyword => fieldCode.includes(keyword) || fieldName.includes(keyword))
})
},
getSiteList() {
getAllSites().then(response => {
const data = response?.data || []
const canAccessAll = !this.belongSite || this.belongSite.length === 0 || this.belongSite.includes('all')
const siteTypeOptions = createSiteTypeOptions().map(item => ({
...item,
children: []
}))
data.forEach(item => {
if (!canAccessAll && !this.belongSite.includes(item.siteId)) return
const siteType = (item.siteType || item.type || 'cn').toString().toLowerCase()
const typeOption = siteTypeOptions.find(i => i.value === siteType) || siteTypeOptions.find(i => i.value === 'cn')
if (!typeOption) return
typeOption.children.push({
text: item.siteName,
value: item.siteId,
id: item.id
})
})
this.siteTypeOptions = siteTypeOptions.filter(item => (item.children || []).length > 0)
const siteChildren = this.siteTypeOptions.reduce((result, typeItem) => {
return result.concat(typeItem.children || [])
}, [])
// 设置默认展示的站点
const defaultSiteId = this.isAvailableSite(this.currentSiteId) ? this.currentSiteId : (siteChildren.find(item =>
!item.disable)?.value || '')
const defaultSiteId = this.isAvailableSite(this.currentSiteId) ? this.currentSiteId : (siteChildren[0]?.value || '')
if (defaultSiteId) {
this.siteId = defaultSiteId
this.$store.commit('SET_CURRENTSITEID', defaultSiteId)
@ -293,94 +403,53 @@
day = time.getDate()
return `${month<10?'0'+month : month}/${day<10 ? '0'+day : day}`
},
getGVQXData() {
this.$refs.activeChartDateRangeSelect.showBtnLoading(true)
getPointData({
siteId: this.siteId,
startDate: this.activeChartTimeRange[0],
endDate: this.activeChartTimeRange[1]
}).then(response => {
console.log('当日功率曲线', response)
let data = response?.data || [],
categories = [],
source = [{
name: '电网功率',
attr: 'gridPower',
data: []
},
{
name: '负载功率',
attr: 'loadPower',
data: []
},
{
name: '储能功率',
attr: 'storagePower',
data: []
},
{
name: '光伏功率',
attr: 'pvPower',
data: []
},
{
name: 'soc平均值',
attr: 'avgSoc',
data: []
},
{
name: 'soh平均值',
attr: 'avgSoh',
data: []
},
{
name: '电池平均温度平均值平均值',
attr: 'avgTemp',
data: []
},
]
data.forEach((item) => {
categories.push(item.statisDate || undefined)
source.forEach(i => i.data.push(item[i.attr]))
})
this.activeChartData = JSON.parse(JSON.stringify({
categories,
series: source
}))
}).finally(() => this.$refs.activeChartDateRangeSelect.showBtnLoading(false))
},
getWeekChartData() {
this.$refs.weekChartDateRangeSelect.showBtnLoading(true)
getSevenChargeData({
siteId: this.siteId,
startDate: this.weekChartTimeRange[0],
endDate: this.weekChartTimeRange[1]
}).then(response => {
console.log('一周功率曲线', response)
let data = response?.data || [],
categories = [],
chargedCap = [],
disChargedCap = []
data.forEach(item => {
categories.push(this.handleDate(item.ammeterDate) || undefined)
chargedCap.push(item.chargedCap)
disChargedCap.push(item.disChargedCap)
})
this.weekChartData = JSON.parse(JSON.stringify({
categories,
series: [{
"name": '充电量',
"data": chargedCap
},
{
"name": '放电量',
"data": disChargedCap
}
]
}))
}).finally(() => this.$refs.weekChartDateRangeSelect.showBtnLoading(false))
getGVQXData() {
this.$refs.activeChartDateRangeSelect.showBtnLoading(true)
const startTime = this.normalizeDateTime(this.activeChartTimeRange[0], false)
const endTime = this.normalizeDateTime(this.activeChartTimeRange[1], true)
this.ensureCurveDisplayData().then(displayData => {
const sectionRows = (displayData || []).filter(item =>
item && item.sectionName === '当日功率曲线' && item.useFixedDisplay !== 1 && item.dataPoint
)
const targetRows = [{
name: '电网功率',
row: this.findActiveCurveRow(sectionRows, ['gridpower', '电网功率'])
},
{
name: '负载功率',
row: this.findActiveCurveRow(sectionRows, ['loadpower', '负载功率'])
},
{
name: '储能功率',
row: this.findActiveCurveRow(sectionRows, ['storagepower', '储能功率'])
}
].filter(item => item.row && item.row.dataPoint)
const tasks = targetRows.map(item => this.fetchCurveSeries(String(item.row.dataPoint).trim(), item.name, startTime,
endTime))
return Promise.all(tasks).then(series => {
this.activeChartData = JSON.parse(JSON.stringify(this.buildLineChartData((series || []).filter(Boolean))))
})
}).finally(() => this.$refs.activeChartDateRangeSelect.showBtnLoading(false))
},
getWeekChartData() {
this.$refs.weekChartDateRangeSelect.showBtnLoading(true)
const startTime = this.normalizeDateTime(this.weekChartTimeRange[0], false)
const endTime = this.normalizeDateTime(this.weekChartTimeRange[1], true)
this.ensureCurveDisplayData().then(displayData => {
const sectionRows = (displayData || []).filter(item =>
item && item.sectionName === '一周充放曲线' && item.useFixedDisplay !== 1 && item.dataPoint
)
const tasks = sectionRows.map(row => {
const pointId = String(row.dataPoint || '').trim()
if (!pointId) return Promise.resolve(null)
const name = row.fieldName || this.getFieldName(row.fieldCode) || pointId
return this.fetchCurveSeries(pointId, name, startTime, endTime)
})
return Promise.all(tasks).then(series => {
this.weekChartData = JSON.parse(JSON.stringify(this.buildLineChartData((series || []).filter(Boolean))))
})
}).finally(() => this.$refs.weekChartDateRangeSelect.showBtnLoading(false))
}
},
watch: {
@ -430,79 +499,8 @@
margin-top: 10rpx;
}
// 站点选择
.site-sections-list {
background: linear-gradient(to right, #547ef4, #679ff5);
padding: 30rpx 30rpx;
padding-bottom: 100rpx;
.info {
color: #fff;
font-size: 26rpx;
line-height: 30rpx;
vertical-align: middle;
margin-top: 20rpx;
>.list {
display: flex;
justify-content: flex-start;
align-items: center;
&:not(:last-child) {
margin-bottom: 20rpx;
}
>.uni-icons {
margin-right: 10rpx;
}
}
}
.uni-data-tree {
::v-deep {
.input-value {
border: none;
padding-left: 0;
.selected-area {
width: 90%;
flex: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
.selected-list {
color: #fff;
}
}
// 选择中的文字样式
.text-color {
color: #fff;
font-size: 34rpx;
line-height: 36rpx;
font-weight: bolder;
}
// 右侧箭头
.arrow-area {
transform: rotate(-135deg);
.input-arrow {
border-color: #fff;
}
}
}
}
}
}
// 基本信息
.base-info {
// 基本信息
.base-info {
margin-top: -80rpx;
border-radius: 80rpx 80rpx 0 0;
padding: 30rpx;