develop #2
@ -104,10 +104,10 @@ export function getSingleSiteBaseInfo(data) {
|
||||
})
|
||||
}
|
||||
|
||||
//单站监控 首页 总累计运行数据
|
||||
export function getDzjkHomeView(siteId) {
|
||||
//单站监控 首页 总累计运行数据(基于日表)
|
||||
export function getDzjkHomeTotalView(siteId) {
|
||||
return request({
|
||||
url: `/ems/siteMonitor/homeView?siteId=${siteId}`,
|
||||
url: `/ems/siteMonitor/homeTotalView?siteId=${siteId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
@ -147,6 +147,18 @@ export function getPointData(data) {
|
||||
})
|
||||
}
|
||||
|
||||
// 点位配置-曲线数据(与管理端一致)
|
||||
export function getPointConfigCurve(data) {
|
||||
return request({
|
||||
url: `/ems/pointConfig/curve`,
|
||||
method: 'post',
|
||||
data,
|
||||
headers: {
|
||||
repeatSubmit: false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取站点包含的设备种类 用来判断单站监控设备监控的菜单栏展示
|
||||
export function getSiteAllDeviceCategory(data) {
|
||||
return request({
|
||||
|
||||
69
components/SiteSelector/index.vue
Normal file
69
components/SiteSelector/index.vue
Normal file
@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<view class="ems-site-selector">
|
||||
<uni-data-picker placeholder="请选择" popup-title="业态选择" :step-searh="true" :value="siteId" :clear-icon="false"
|
||||
:localdata="siteTypeOptions" :ellipsis="false" @change="handleChange">
|
||||
</uni-data-picker>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters(['siteId', 'siteTypeOptions'])
|
||||
},
|
||||
methods: {
|
||||
handleChange(data) {
|
||||
const valueList = data?.detail?.value || []
|
||||
const siteObj = valueList[1]
|
||||
if (!siteObj || siteObj.value === undefined || siteObj.value === null || siteObj.value === this.siteId) return
|
||||
this.$store.dispatch('SetCurrentSiteId', siteObj.value)
|
||||
this.$emit('change', siteObj.value)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.dispatch('LoadSites')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ems-site-selector {
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
128
components/SiteSwitchHeader/index.vue
Normal file
128
components/SiteSwitchHeader/index.vue
Normal file
@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<view class="site-switch-header">
|
||||
<uni-data-picker
|
||||
placeholder="请选择"
|
||||
popup-title="业态选择"
|
||||
:step-searh="true"
|
||||
:value="siteId"
|
||||
:clear-icon="false"
|
||||
:localdata="siteTypeOptions"
|
||||
:ellipsis="false"
|
||||
@change="handleChange"
|
||||
/>
|
||||
<view class="info">
|
||||
<view class="list">
|
||||
<uni-icons type="location" color="#fff" size="20"></uni-icons>
|
||||
{{ siteAddress || '-' }}
|
||||
</view>
|
||||
<view class="list">
|
||||
<uni-icons type="calendar" color="#fff" size="20"></uni-icons>
|
||||
{{ runningTime || '-' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
siteId: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
siteTypeOptions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
siteAddress: {
|
||||
type: String,
|
||||
default: '-'
|
||||
},
|
||||
runningTime: {
|
||||
type: String,
|
||||
default: '-'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleChange(data) {
|
||||
this.$emit('change', data)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.site-switch-header {
|
||||
background: linear-gradient(to right, #547ef4, #679ff5);
|
||||
padding: 30rpx 30rpx;
|
||||
padding-bottom: 100rpx;
|
||||
color: #fff;
|
||||
|
||||
.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 {
|
||||
.uni-data-tree-dialog {
|
||||
color: #333;
|
||||
|
||||
.selected-item,
|
||||
.dialog-title {
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,7 +1,7 @@
|
||||
// 应用全局配置
|
||||
module.exports = {
|
||||
// todo 打包项目时切换baseUrl
|
||||
//baseUrl: 'http://localhost:8089',
|
||||
// baseUrl: 'http://localhost:8089',
|
||||
// 测试环境
|
||||
baseUrl: 'http://110.40.171.179:8089',
|
||||
// 生产环境
|
||||
|
||||
137
pages/index.vue
137
pages/index.vue
@ -1,20 +1,7 @@
|
||||
<template>
|
||||
<view class="home-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>
|
||||
<site-switch-header :site-id="siteId" :site-type-options="siteTypeOptions" :site-address="baseInfo.siteAddress"
|
||||
:running-time="baseInfo.runningTime" @change="selectedSite" />
|
||||
|
||||
<view class="base-info">
|
||||
<view class="map-card">
|
||||
@ -63,17 +50,12 @@
|
||||
import {
|
||||
getAllSites,
|
||||
getSingleSiteBaseInfo,
|
||||
getDzjkHomeView,
|
||||
getDzjkHomeTotalView,
|
||||
getProjectDisplayData,
|
||||
getAmmeterRevenueData
|
||||
} from '@/api/ems/site.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
pageScrollTop: 0,
|
||||
siteOptions: [],
|
||||
siteTypeOptions: [{
|
||||
import SiteSwitchHeader from '@/components/SiteSwitchHeader/index.vue'
|
||||
const createSiteTypeOptions = () => ([{
|
||||
text: '储能',
|
||||
value: 'cn',
|
||||
children: []
|
||||
@ -81,16 +63,24 @@
|
||||
{
|
||||
text: '光能',
|
||||
value: 'gn',
|
||||
disable: true,
|
||||
children: []
|
||||
},
|
||||
{
|
||||
text: '岸电',
|
||||
value: 'ad',
|
||||
disable: true,
|
||||
children: []
|
||||
}
|
||||
],
|
||||
])
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SiteSwitchHeader
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pageScrollTop: 0,
|
||||
siteOptions: [],
|
||||
siteTypeOptions: createSiteTypeOptions(),
|
||||
siteId: '',
|
||||
mapUrl: '',
|
||||
baseInfo: {},
|
||||
@ -251,21 +241,29 @@
|
||||
getSiteList() {
|
||||
getAllSites().then(response => {
|
||||
const data = response?.data || []
|
||||
const children = data.map(item => {
|
||||
return {
|
||||
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,
|
||||
longitude: Number(item.longitude || 0),
|
||||
latitude: Number(item.latitude || 0),
|
||||
disable: !this.belongSite || this.belongSite.length === 0 || this.belongSite.includes('all') ? false : !this
|
||||
.belongSite.includes(item.siteId)
|
||||
}
|
||||
latitude: Number(item.latitude || 0)
|
||||
})
|
||||
this.siteTypeOptions.find(i => i.value === 'cn').children = children
|
||||
this.siteOptions = children
|
||||
const defaultSiteId = this.isAvailableSite(this.currentSiteId) ? this.currentSiteId : (children.find(item => !item
|
||||
.disable)?.value || '')
|
||||
})
|
||||
this.siteTypeOptions = siteTypeOptions.filter(item => (item.children || []).length > 0)
|
||||
this.siteOptions = this.siteTypeOptions.reduce((result, typeItem) => {
|
||||
return result.concat(typeItem.children || [])
|
||||
}, [])
|
||||
const defaultSiteId = this.isAvailableSite(this.currentSiteId) ? this.currentSiteId : (this.siteOptions[0]?.value || '')
|
||||
if (defaultSiteId) {
|
||||
this.siteId = defaultSiteId
|
||||
this.$store.commit('SET_CURRENTSITEID', defaultSiteId)
|
||||
@ -290,7 +288,7 @@
|
||||
},
|
||||
getRunningInfo() {
|
||||
return Promise.all([
|
||||
getDzjkHomeView(this.siteId),
|
||||
getDzjkHomeTotalView(this.siteId),
|
||||
getProjectDisplayData(this.siteId)
|
||||
]).then(([homeResponse, displayResponse]) => {
|
||||
this.runningInfo = homeResponse?.data || {}
|
||||
@ -416,71 +414,6 @@
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.site-sections-list {
|
||||
background: linear-gradient(to right, #547ef4, #679ff5);
|
||||
padding: 30rpx 30rpx;
|
||||
padding-bottom: 100rpx;
|
||||
color: #fff;
|
||||
|
||||
.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 {
|
||||
margin-top: -80rpx;
|
||||
border-radius: 80rpx 80rpx 0 0;
|
||||
|
||||
@ -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>
|
||||
<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>
|
||||
@ -70,16 +58,34 @@
|
||||
mapGetters
|
||||
} from 'vuex'
|
||||
import DateRangeSelect from './DateRangeSelect.vue'
|
||||
import SiteSwitchHeader from '@/components/SiteSwitchHeader/index.vue'
|
||||
import {
|
||||
getAllSites,
|
||||
getSingleSiteBaseInfo,
|
||||
getSevenChargeData,
|
||||
getPointData,
|
||||
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
|
||||
DateRangeSelect,
|
||||
SiteSwitchHeader
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -88,6 +94,8 @@
|
||||
activeChartTimeRange: [],
|
||||
weekChartData: {},
|
||||
activeChartData: {},
|
||||
curveDisplayData: [],
|
||||
curveDisplayLoadingPromise: null,
|
||||
pageScrollTop: 0,
|
||||
glqxOptions: {
|
||||
padding: [10, 5, 0, 10],
|
||||
@ -141,24 +149,7 @@
|
||||
},
|
||||
// 图表数据结束
|
||||
deviceCategoryOptions: [], //当前站点包含的设备类别
|
||||
siteTypeOptions: [{
|
||||
text: '储能',
|
||||
value: 'cn',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
text: '光能',
|
||||
value: 'gn',
|
||||
disable: true,
|
||||
children: []
|
||||
},
|
||||
{
|
||||
text: '岸电',
|
||||
value: 'ad',
|
||||
disable: true,
|
||||
children: []
|
||||
}
|
||||
],
|
||||
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)
|
||||
},
|
||||
// 更新一周冲放曲线时间范围 重置图表
|
||||
@ -242,28 +236,144 @@
|
||||
},
|
||||
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 {
|
||||
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 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 || []
|
||||
this.siteTypeOptions.find(i => i.value === 'cn').children = data.map(item => {
|
||||
return {
|
||||
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,
|
||||
disable: !this.belongSite || this.belongSite.length === 0 || this
|
||||
.belongSite.includes('all') ? false : !this.belongSite.includes(item
|
||||
.siteId)
|
||||
}
|
||||
id: item.id
|
||||
})
|
||||
const siteChildren = this.siteTypeOptions.find(i => i.value === 'cn')?.children || []
|
||||
})
|
||||
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)
|
||||
@ -295,91 +405,50 @@
|
||||
},
|
||||
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 = [{
|
||||
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: '电网功率',
|
||||
attr: 'gridPower',
|
||||
data: []
|
||||
row: this.findActiveCurveRow(sectionRows, ['gridpower', '电网功率'])
|
||||
},
|
||||
{
|
||||
name: '负载功率',
|
||||
attr: 'loadPower',
|
||||
data: []
|
||||
row: this.findActiveCurveRow(sectionRows, ['loadpower', '负载功率'])
|
||||
},
|
||||
{
|
||||
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]))
|
||||
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))))
|
||||
})
|
||||
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)
|
||||
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))))
|
||||
})
|
||||
this.weekChartData = JSON.parse(JSON.stringify({
|
||||
categories,
|
||||
series: [{
|
||||
"name": '充电量',
|
||||
"data": chargedCap
|
||||
},
|
||||
{
|
||||
"name": '放电量',
|
||||
"data": disChargedCap
|
||||
}
|
||||
]
|
||||
}))
|
||||
}).finally(() => this.$refs.weekChartDateRangeSelect.showBtnLoading(false))
|
||||
}
|
||||
},
|
||||
@ -430,77 +499,6 @@
|
||||
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 {
|
||||
margin-top: -80rpx;
|
||||
|
||||
109
store/modules/site.js
Normal file
109
store/modules/site.js
Normal file
@ -0,0 +1,109 @@
|
||||
import { getAllSites } from '@/api/ems/site.js'
|
||||
|
||||
const createSiteTypeOptions = () => ([
|
||||
{
|
||||
text: '储能',
|
||||
value: 'cn',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
text: '光能',
|
||||
value: 'gn',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
text: '岸电',
|
||||
value: 'ad',
|
||||
children: []
|
||||
}
|
||||
])
|
||||
|
||||
const site = {
|
||||
state: {
|
||||
siteOptions: [],
|
||||
siteTypeOptions: [],
|
||||
currentSiteId: '',
|
||||
loaded: false
|
||||
},
|
||||
mutations: {
|
||||
SET_SITE_OPTIONS: (state, siteOptions) => {
|
||||
state.siteOptions = siteOptions || []
|
||||
},
|
||||
SET_SITE_TYPE_OPTIONS: (state, siteTypeOptions) => {
|
||||
state.siteTypeOptions = siteTypeOptions || createSiteTypeOptions()
|
||||
},
|
||||
SET_CURRENT_SITE_ID: (state, siteId) => {
|
||||
state.currentSiteId = siteId || ''
|
||||
},
|
||||
SET_SITE_LOADED: (state, loaded) => {
|
||||
state.loaded = !!loaded
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
LoadSites({ state, rootState, commit }, payload = {}) {
|
||||
const { force = false } = payload
|
||||
if (state.loaded && !force) {
|
||||
return Promise.resolve({
|
||||
siteOptions: state.siteOptions,
|
||||
siteTypeOptions: state.siteTypeOptions,
|
||||
siteId: state.currentSiteId
|
||||
})
|
||||
}
|
||||
return getAllSites().then(response => {
|
||||
const belongSite = rootState?.user?.belongSite || []
|
||||
const canAccessAll = !belongSite || belongSite.length === 0 || belongSite.includes('all')
|
||||
const siteOptions = (response?.data || []).filter(item => {
|
||||
const siteId = item.siteId || ''
|
||||
return canAccessAll || belongSite.includes(siteId)
|
||||
}).map(item => {
|
||||
const siteId = item.siteId || ''
|
||||
const siteType = (item.siteType || item.type || 'cn').toString().toLowerCase()
|
||||
return {
|
||||
text: item.siteName,
|
||||
value: siteId,
|
||||
id: item.id,
|
||||
longitude: Number(item.longitude || 0),
|
||||
latitude: Number(item.latitude || 0),
|
||||
siteType
|
||||
}
|
||||
})
|
||||
const siteTypeOptions = createSiteTypeOptions().map(typeItem => ({
|
||||
...typeItem,
|
||||
children: []
|
||||
}))
|
||||
siteOptions.forEach(item => {
|
||||
const typeOption = siteTypeOptions.find(i => i.value === item.siteType) || siteTypeOptions.find(i => i.value === 'cn')
|
||||
if (!typeOption) return
|
||||
typeOption.children.push({
|
||||
text: item.text,
|
||||
value: item.value,
|
||||
id: item.id
|
||||
})
|
||||
})
|
||||
const filteredSiteTypeOptions = siteTypeOptions.filter(item => (item.children || []).length > 0)
|
||||
const availableSite = siteOptions[0]
|
||||
const keepCurrent = siteOptions.find(item => item.value === state.currentSiteId)
|
||||
commit('SET_SITE_OPTIONS', siteOptions)
|
||||
commit('SET_SITE_TYPE_OPTIONS', filteredSiteTypeOptions)
|
||||
commit('SET_CURRENT_SITE_ID', keepCurrent ? keepCurrent.value : (availableSite?.value || ''))
|
||||
commit('SET_SITE_LOADED', true)
|
||||
return {
|
||||
siteOptions,
|
||||
siteTypeOptions: filteredSiteTypeOptions,
|
||||
siteId: keepCurrent ? keepCurrent.value : (availableSite?.value || '')
|
||||
}
|
||||
})
|
||||
},
|
||||
SetCurrentSiteId({ commit }, siteId) {
|
||||
commit('SET_CURRENT_SITE_ID', siteId)
|
||||
},
|
||||
ClearSiteState({ commit }) {
|
||||
commit('SET_SITE_OPTIONS', [])
|
||||
commit('SET_SITE_TYPE_OPTIONS', [])
|
||||
commit('SET_CURRENT_SITE_ID', '')
|
||||
commit('SET_SITE_LOADED', false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default site
|
||||
@ -21,16 +21,25 @@ const request = config => {
|
||||
config.url = url
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const requestUrl = config.baseUrl || baseUrl + config.url
|
||||
const requestMethod = (config.method || 'get').toUpperCase()
|
||||
uni.request({
|
||||
method: config.method || 'get',
|
||||
timeout: config.timeout || timeout,
|
||||
url: config.baseUrl || baseUrl + config.url,
|
||||
url: requestUrl,
|
||||
data: config.data,
|
||||
header: config.header,
|
||||
dataType: 'json'
|
||||
}).then(response => {
|
||||
let [error, res] = response
|
||||
if (error) {
|
||||
const errorType = error?.errMsg || error?.message || 'UNKNOWN_ERROR'
|
||||
console.error('[request:error]', {
|
||||
url: requestUrl,
|
||||
method: requestMethod,
|
||||
errorType,
|
||||
rawError: error
|
||||
})
|
||||
toast('后端接口连接异常')
|
||||
reject('后端接口连接异常')
|
||||
return
|
||||
@ -57,6 +66,15 @@ const request = config => {
|
||||
})
|
||||
.catch(error => {
|
||||
let { message } = error
|
||||
const rawMessage = message || ''
|
||||
let errorType = 'UNKNOWN_ERROR'
|
||||
if (rawMessage === 'Network Error') {
|
||||
errorType = 'NETWORK_ERROR'
|
||||
} else if (rawMessage.includes('timeout')) {
|
||||
errorType = 'TIMEOUT'
|
||||
} else if (rawMessage.includes('Request failed with status code')) {
|
||||
errorType = 'HTTP_STATUS_ERROR'
|
||||
}
|
||||
if (message === 'Network Error') {
|
||||
message = '后端接口连接异常'
|
||||
} else if (message.includes('timeout')) {
|
||||
@ -64,6 +82,13 @@ const request = config => {
|
||||
} else if (message.includes('Request failed with status code')) {
|
||||
message = '系统接口' + message.substr(message.length - 3) + '异常'
|
||||
}
|
||||
console.error('[request:catch]', {
|
||||
url: requestUrl,
|
||||
method: requestMethod,
|
||||
errorType,
|
||||
message: rawMessage,
|
||||
rawError: error
|
||||
})
|
||||
toast(message)
|
||||
reject(error)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user