|
|
|
@ -1,5 +1,6 @@ |
|
|
|
<template> |
|
|
|
<div class="shore-power-usage"> |
|
|
|
<template v-if="pageType === 'realtime'"> |
|
|
|
<!-- Average Mode --> |
|
|
|
<template v-if="displayMode === 'average'"> |
|
|
|
<div class="left average" style="width: 40%;"> |
|
|
|
@ -12,17 +13,27 @@ |
|
|
|
<span class="title-text">{{ card.title }}</span> |
|
|
|
</div> |
|
|
|
<div v-if="card.type === 'overview'" class="time-range-selector"> |
|
|
|
<el-date-picker type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" |
|
|
|
format="YYYY-MM-DD" value-format="YYYY-MM-DD" class="date-range-picker" @click.stop /> |
|
|
|
<button v-for="option in timeRangeOptions" :key="option.value" |
|
|
|
:class="['time-range-btn', { active: timeRange === option.value }]" |
|
|
|
@click.stop="handleTimeRangeChange(option.value)"> |
|
|
|
<el-date-picker v-if="historyTimeGranularity === 'year'" type="yearrange" range-separator="至" |
|
|
|
start-placeholder="开始年" end-placeholder="结束年" format="YYYY" value-format="YYYY" |
|
|
|
class="date-range-picker" @click.stop /> |
|
|
|
<el-date-picker v-else-if="historyTimeGranularity === 'month'" type="monthrange" range-separator="至" |
|
|
|
start-placeholder="开始月" end-placeholder="结束月" format="YYYY-MM" value-format="YYYY-MM" |
|
|
|
class="date-range-picker" @click.stop /> |
|
|
|
<!-- <el-date-picker v-else-if="historyTimeGranularity === 'week'" type="weekrange" range-separator="至" |
|
|
|
start-placeholder="开始周" end-placeholder="结束周" format="YYYY-MM-DD" value-format="YYYY-MM-DD" |
|
|
|
class="date-range-picker" @click.stop /> --> |
|
|
|
<el-date-picker v-else v-model="dateRange" type="daterange" range-separator="至" start-placeholder="开始日" |
|
|
|
end-placeholder="结束日" format="YYYY-MM-DD" value-format="YYYY-MM-DD" class="date-range-picker" /> |
|
|
|
<button v-for="option in historyTimeGranularityOptions" :key="option.value" |
|
|
|
:class="['time-range-btn', { active: historyTimeGranularity === option.value }]" |
|
|
|
@click.stop="handleHistoryTimeGranularityChange(option.value as 'year' | 'month' | 'week' | 'day')"> |
|
|
|
{{ option.label }} |
|
|
|
</button> |
|
|
|
<el-button type="primary" @click.stop="handleDateRangeConfirm">选择并确认</el-button> |
|
|
|
</div> |
|
|
|
<div v-if="card.type === 'chart'" class="show-value"> |
|
|
|
<span class="show-value-label">{{timeRangeOptions.find(option => option.value === timeRange)?.label || |
|
|
|
''}}总量:</span> |
|
|
|
''}}量:</span> |
|
|
|
<div class="show-value-value">{{ chartData[timeRange][card.value] }}</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
@ -67,17 +78,28 @@ |
|
|
|
<span class="title-text">{{ card.title }}</span> |
|
|
|
</div> |
|
|
|
<div v-if="card.type === 'overview'" class="time-range-selector"> |
|
|
|
<el-date-picker type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" |
|
|
|
format="YYYY-MM-DD" value-format="YYYY-MM-DD" class="date-range-picker" @click.stop /> |
|
|
|
<button v-for="option in timeRangeOptions" :key="option.value" |
|
|
|
:class="['time-range-btn', { active: timeRange === option.value }]" |
|
|
|
@click.stop="handleTimeRangeChange(option.value)"> |
|
|
|
<el-date-picker v-if="historyTimeGranularity === 'year'" type="yearrange" range-separator="至" |
|
|
|
start-placeholder="开始年" end-placeholder="结束年" format="YYYY" value-format="YYYY" |
|
|
|
class="date-range-picker" @click.stop /> |
|
|
|
<el-date-picker v-else-if="historyTimeGranularity === 'month'" type="monthrange" range-separator="至" |
|
|
|
start-placeholder="开始月" end-placeholder="结束月" format="YYYY-MM" value-format="YYYY-MM" |
|
|
|
class="date-range-picker" @click.stop /> |
|
|
|
<!-- <el-date-picker v-else-if="historyTimeGranularity === 'week'" type="weekrange" range-separator="至" |
|
|
|
start-placeholder="开始周" end-placeholder="结束周" format="YYYY-MM-DD" value-format="YYYY-MM-DD" |
|
|
|
class="date-range-picker" @click.stop /> --> |
|
|
|
<el-date-picker v-else type="daterange" range-separator="至" start-placeholder="开始日" |
|
|
|
end-placeholder="结束日" format="YYYY-MM-DD" value-format="YYYY-MM-DD" class="date-range-picker" |
|
|
|
@click.stop /> |
|
|
|
<button v-for="option in historyTimeGranularityOptions" :key="option.value" |
|
|
|
:class="['time-range-btn', { active: historyTimeGranularity === option.value }]" |
|
|
|
@click.stop="handleHistoryTimeGranularityChange(option.value as 'year' | 'month' | 'week' | 'day')"> |
|
|
|
{{ option.label }} |
|
|
|
</button> |
|
|
|
<el-button type="primary" @click.stop="handleDateRangeConfirm">选择并确认</el-button> |
|
|
|
</div> |
|
|
|
<div v-if="card.type === 'chart'" class="show-value"> |
|
|
|
<span class="show-value-label">{{timeRangeOptions.find(option => option.value === timeRange)?.label || |
|
|
|
''}}总量:</span> |
|
|
|
''}}量:</span> |
|
|
|
<div class="show-value-value">{{ chartData[timeRange][card.value] }}</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
@ -126,8 +148,7 @@ |
|
|
|
<span class="title-text">{{ card.title }}</span> |
|
|
|
</div> |
|
|
|
<div v-if="card.type === 'overview'" class="time-range-selector"> |
|
|
|
<el-date-picker type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" |
|
|
|
format="YYYY-MM-DD" value-format="YYYY-MM-DD" class="date-range-picker" /> |
|
|
|
<el-button type="primary" @click.stop="pageType = 'history'">历史查询</el-button> |
|
|
|
<button v-for="option in timeRangeOptions" :key="option.value" |
|
|
|
:class="['time-range-btn', { active: timeRange === option.value }]" |
|
|
|
@click.stop="handleTimeRangeChange(option.value)"> |
|
|
|
@ -136,7 +157,7 @@ |
|
|
|
</div> |
|
|
|
<div v-if="card.type === 'chart'" class="show-value"> |
|
|
|
<span class="show-value-label">{{timeRangeOptions.find(option => option.value === timeRange)?.label || |
|
|
|
''}}总量:</span> |
|
|
|
''}}量:</span> |
|
|
|
<div class="show-value-value">{{ chartData[timeRange][card.value] }}</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
@ -190,7 +211,7 @@ |
|
|
|
</div> |
|
|
|
<div v-if="card.type === 'chart'" class="show-value"> |
|
|
|
<span class="show-value-label">{{timeRangeOptions.find(option => option.value === timeRange)?.label || |
|
|
|
''}}总量:</span> |
|
|
|
''}}量:</span> |
|
|
|
<div class="show-value-value">{{ chartData[timeRange][card.value] }}</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
@ -226,6 +247,251 @@ |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
<template v-if="pageType === 'history'"> |
|
|
|
<!-- Average Mode --> |
|
|
|
<template v-if="displayMode === 'average'"> |
|
|
|
<div class="left average" style="width: 40%;"> |
|
|
|
<div v-for="card in cards.slice(0, 3)" :key="card.id" class="card digital-twin-card--deep-blue" |
|
|
|
@click="handleSelectCard(card.id)"> |
|
|
|
<div style="display: flex; align-items: center; justify-content: space-between;"> |
|
|
|
<div class="card-title"> |
|
|
|
<div class="vertical-line"></div> |
|
|
|
<img src="@/assets/svgs/data.svg" class="title-icon" /> |
|
|
|
<span class="title-text">{{ card.title }}</span> |
|
|
|
</div> |
|
|
|
<div v-if="card.type === 'overview'" class="time-range-selector"> |
|
|
|
<button v-for="option in historyTimeGranularityOptions" :key="option.value" |
|
|
|
:class="['time-range-btn', { active: historyTimeGranularity === option.value }]" |
|
|
|
@click.stop="handleHistoryTimeGranularityChange(option.value as 'year' | 'month' | 'week' | 'day')"> |
|
|
|
{{ option.label }} |
|
|
|
</button> |
|
|
|
<el-button type="primary" @click.stop="handleDateRangeConfirm">选择并确认</el-button> |
|
|
|
</div> |
|
|
|
<div v-if="card.type === 'chart'" class="show-value"> |
|
|
|
<span class="show-value-label">{{timeRangeOptions.find(option => option.value === timeRange)?.label || |
|
|
|
''}}量:</span> |
|
|
|
<div class="show-value-value">{{ chartData[timeRange][card.value] }}</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div class="card-content"> |
|
|
|
<div v-if="card.type === 'overview'" class="overview-grid"> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ chartData[timeRange].totalPower }}</div> |
|
|
|
<div class="overview-label">累计用电(千瓦时)</div> |
|
|
|
</div> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ chartData[timeRange].fuel }}</div> |
|
|
|
<div class="overview-label">减少燃油(吨)</div> |
|
|
|
</div> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ chartData[timeRange].co2 }}</div> |
|
|
|
<div class="overview-label">减少二氧化碳排放(千克)</div> |
|
|
|
</div> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ chartData[timeRange].pm25 }}</div> |
|
|
|
<div class="overview-label">减少PM2.5排放(千克)</div> |
|
|
|
</div> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ chartData[timeRange].nox }}</div> |
|
|
|
<div class="overview-label">减少氮氧化物(千克)</div> |
|
|
|
</div> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ chartData[timeRange].so2 }}</div> |
|
|
|
<div class="overview-label">减少二氧化硫(千克)</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<WaveLineChart v-else :chart-data="getChartData(card.id)" :title="card.title" :color="card.color" /> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div class="right average" style="width: 40%;"> |
|
|
|
<div v-for="card in cards.slice(3)" :key="card.id" class="card digital-twin-card--deep-blue" |
|
|
|
@click="handleSelectCard(card.id)"> |
|
|
|
<div style="display: flex; align-items: center; justify-content: space-between;"> |
|
|
|
<div class="card-title"> |
|
|
|
<div class="vertical-line"></div> |
|
|
|
<img src="@/assets/svgs/data.svg" class="title-icon" /> |
|
|
|
<span class="title-text">{{ card.title }}</span> |
|
|
|
</div> |
|
|
|
<div v-if="card.type === 'overview'" class="time-range-selector"> |
|
|
|
<el-date-picker type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" |
|
|
|
format="YYYY-MM-DD" value-format="YYYY-MM-DD" class="date-range-picker" @click.stop /> |
|
|
|
<button v-for="option in timeRangeOptions" :key="option.value" |
|
|
|
:class="['time-range-btn', { active: timeRange === option.value }]" |
|
|
|
@click.stop="handleTimeRangeChange(option.value)"> |
|
|
|
{{ option.label }} |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
<div v-if="card.type === 'chart'" class="show-value"> |
|
|
|
<span class="show-value-label">{{timeRangeOptions.find(option => option.value === timeRange)?.label || |
|
|
|
''}}量:</span> |
|
|
|
<div class="show-value-value">{{ chartData[timeRange][card.value] }}</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div class="card-content"> |
|
|
|
<div v-if="card.type === 'overview'" class="overview-grid"> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ totalPower }}</div> |
|
|
|
<div class="overview-label">累计用电(千瓦时)</div> |
|
|
|
</div> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ fuelReduction }}</div> |
|
|
|
<div class="overview-label">减少燃油(吨)</div> |
|
|
|
</div> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ co2Reduction }}</div> |
|
|
|
<div class="overview-label">减少二氧化碳排放(千克)</div> |
|
|
|
</div> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ pm25Reduction }}</div> |
|
|
|
<div class="overview-label">减少PM2.5排放(千克)</div> |
|
|
|
</div> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ noxReduction }}</div> |
|
|
|
<div class="overview-label">减少氮氧化物(千克)</div> |
|
|
|
</div> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ so2Reduction }}</div> |
|
|
|
<div class="overview-label">减少二氧化硫(千克)</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<WaveLineChart v-else :chart-data="getChartData(card.id)" :title="card.title" :color="card.color" /> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
<!-- Magnify Mode --> |
|
|
|
<div v-if="displayMode === 'magnify'" class="magnify"> |
|
|
|
<div class="big"> |
|
|
|
<div v-for="card in cards.filter(c => c.id === selectedCard)" :key="card.id" |
|
|
|
class="card digital-twin-card--deep-blue"> |
|
|
|
<div style="display: flex; align-items: center; justify-content: space-between;"> |
|
|
|
<div class="card-title"> |
|
|
|
<div class="vertical-line"></div> |
|
|
|
<img src="@/assets/svgs/data.svg" class="title-icon" /> |
|
|
|
<span class="title-text">{{ card.title }}</span> |
|
|
|
</div> |
|
|
|
<div v-if="card.type === 'overview'" class="time-range-selector"> |
|
|
|
<el-date-picker v-if="historyTimeGranularity === 'year'" v-model="dateRange" type="yearrange" |
|
|
|
range-separator="至" start-placeholder="开始年" end-placeholder="结束年" format="YYYY" value-format="YYYY" |
|
|
|
class="date-range-picker" @change="handleSelectDate" /> |
|
|
|
<el-date-picker v-else-if="historyTimeGranularity === 'month'" v-model="dateRange" type="monthrange" |
|
|
|
range-separator="至" start-placeholder="开始月" end-placeholder="结束月" format="YYYY-MM" |
|
|
|
value-format="YYYY-MM" class="date-range-picker" @change="handleSelectDate" /> |
|
|
|
<!-- <el-date-picker v-else-if="historyTimeGranularity === 'week'" v-model="dateRange" type="week" |
|
|
|
range-separator="至" start-placeholder="开始周" end-placeholder="结束周" format="YYYY-MM-DD" |
|
|
|
value-format="YYYY-MM-DD" class="date-range-picker" @change="handleSelectDate" /> --> |
|
|
|
<el-date-picker v-else v-model="dateRange" type="daterange" range-separator="至" start-placeholder="开始日" |
|
|
|
end-placeholder="结束日" format="YYYY-MM-DD" value-format="YYYY-MM-DD" class="date-range-picker" |
|
|
|
@change="handleSelectDate" /> |
|
|
|
<button v-for="option in historyTimeGranularityOptions" :key="option.value" |
|
|
|
:class="['time-range-btn', { active: historyTimeGranularity === option.value }]" |
|
|
|
@click.stop="handleHistoryTimeGranularityChange(option.value as 'year' | 'month' | 'week' | 'day')"> |
|
|
|
{{ option.label }} |
|
|
|
</button> |
|
|
|
<el-button type="primary" @click.stop="pageType = 'realtime'">实时显示</el-button> |
|
|
|
<!-- <button v-for="option in timeRangeOptions" :key="option.value" |
|
|
|
:class="['time-range-btn', { active: timeRange === option.value }]" |
|
|
|
@click.stop="handleTimeRangeChange(option.value)"> |
|
|
|
{{ option.label }} |
|
|
|
</button> --> |
|
|
|
</div> |
|
|
|
<div v-if="card.type === 'chart'" class="show-value"> |
|
|
|
<span class="show-value-label">{{timeRangeOptions.find(option => option.value === timeRange)?.label || |
|
|
|
''}}量:</span> |
|
|
|
<div class="show-value-value">{{ chartData[timeRange][card.value] }}</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div class="card-content"> |
|
|
|
<div v-if="card.type === 'overview'" class="overview-grid"> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ historyData.totalPower }}</div> |
|
|
|
<div class="overview-label">区间用电(千瓦时)</div> |
|
|
|
</div> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ historyData.fuel }}</div> |
|
|
|
<div class="overview-label">减少燃油(吨)</div> |
|
|
|
</div> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ historyData.co2 }}</div> |
|
|
|
<div class="overview-label">减少二氧化碳排放(千克)</div> |
|
|
|
</div> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ historyData.pm25 }}</div> |
|
|
|
<div class="overview-label">减少PM2.5排放(千克)</div> |
|
|
|
</div> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ historyData.nox }}</div> |
|
|
|
<div class="overview-label">减少氮氧化物(千克)</div> |
|
|
|
</div> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ historyData.so2 }}</div> |
|
|
|
<div class="overview-label">减少二氧化硫(千克)</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<WaveLineChart v-else :chart-data="getHistoryChartData(card.id)" :title="card.title" :color="card.color" |
|
|
|
:magnify-mode="true" /> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div class="one-row"> |
|
|
|
<div v-for="card in cards.filter(c => c.id !== selectedCard)" :key="card.id" |
|
|
|
class="card digital-twin-card--deep-blue" @click="handleSelectCard(card.id)"> |
|
|
|
<div style="display: flex; align-items: center; justify-content: space-between;"> |
|
|
|
<div class="card-title"> |
|
|
|
<div class="vertical-line"></div> |
|
|
|
<img src="@/assets/svgs/data.svg" class="title-icon" /> |
|
|
|
<span class="title-text">{{ card.title }}</span> |
|
|
|
</div> |
|
|
|
<div v-if="card.type === 'overview'" class="time-range-selector"> |
|
|
|
<!-- <button v-for="option in timeRangeOptions" :key="option.value" |
|
|
|
:class="['time-range-btn', { active: timeRange === option.value }]" |
|
|
|
@click.stop="handleTimeRangeChange(option.value)"> |
|
|
|
{{ option.label }} |
|
|
|
</button> --> |
|
|
|
</div> |
|
|
|
<div v-if="card.type === 'chart'" class="show-value"> |
|
|
|
<span class="show-value-label">{{timeRangeOptions.find(option => option.value === timeRange)?.label || |
|
|
|
''}}量:</span> |
|
|
|
<div class="show-value-value">{{ chartData[timeRange][card.value] }}</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div class="card-content"> |
|
|
|
<div v-if="card.type === 'overview'" class="overview-grid"> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ historyData.totalPower }}</div> |
|
|
|
<div class="overview-label">区间用电(千瓦时)</div> |
|
|
|
</div> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ historyData.fuel }}</div> |
|
|
|
<div class="overview-label">减少燃油(吨)</div> |
|
|
|
</div> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ historyData.co2 }}</div> |
|
|
|
<div class="overview-label">减少二氧化碳排放(千克)</div> |
|
|
|
</div> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ historyData.pm25 }}</div> |
|
|
|
<div class="overview-label">减少PM2.5排放(千克)</div> |
|
|
|
</div> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ historyData.nox }}</div> |
|
|
|
<div class="overview-label">减少氮氧化物(千克)</div> |
|
|
|
</div> |
|
|
|
<div class="overview-item"> |
|
|
|
<div class="overview-value">{{ historyData.so2 }}</div> |
|
|
|
<div class="overview-label">减少二氧化硫(千克)</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<WaveLineChart v-else :chart-data="getHistoryChartData(card.id)" :title="card.title" |
|
|
|
:color="card.color" /> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
@ -235,6 +501,7 @@ import WaveLineChart from './charts/WaveLineChart.vue' |
|
|
|
import { MapApi } from "@/api/shorepower/map"; |
|
|
|
import dayjs from 'dayjs'; |
|
|
|
import { RealtimeDeviceData } from '@/types/shorepower'; |
|
|
|
import { formatTimestamp, parseRangeToTimestamp } from './utils'; |
|
|
|
|
|
|
|
|
|
|
|
interface Props { |
|
|
|
@ -316,6 +583,8 @@ export interface deviceData { |
|
|
|
|
|
|
|
// const props = defineProps<Props>() |
|
|
|
|
|
|
|
const pageType = ref<'realtime' | 'history'>('realtime') |
|
|
|
const dateRange = ref<string[]>([]) |
|
|
|
const totalPower = ref<string>('0') |
|
|
|
const fuelReduction = ref<string>('0') |
|
|
|
const co2Reduction = ref<string>('0') |
|
|
|
@ -336,6 +605,20 @@ const noxReductionChartData = ref<ChartDataItem[]>([]) |
|
|
|
const pm25ReductionChartData = ref<ChartDataItem[]>([]) |
|
|
|
const fuelReductionChartData = ref<ChartDataItem[]>([]) |
|
|
|
|
|
|
|
const h_totalPowerChartData = ref<ChartDataItem[]>([]) |
|
|
|
const h_co2ReductionChartData = ref<ChartDataItem[]>([]) |
|
|
|
const h_so2ReductionChartData = ref<ChartDataItem[]>([]) |
|
|
|
const h_noxReductionChartData = ref<ChartDataItem[]>([]) |
|
|
|
const h_pm25ReductionChartData = ref<ChartDataItem[]>([]) |
|
|
|
const h_fuelReductionChartData = ref<ChartDataItem[]>([]) |
|
|
|
const historyData = ref({ |
|
|
|
'totalPower': 0, |
|
|
|
'fuel': 0, |
|
|
|
'co2': 0, |
|
|
|
'pm25': 0, |
|
|
|
'nox': 0, |
|
|
|
'so2': 0, |
|
|
|
}) |
|
|
|
const chartData = ref({ |
|
|
|
'realtime': { |
|
|
|
'totalPower': 0, |
|
|
|
@ -388,6 +671,144 @@ const chartData = ref({ |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
function aggregateByIndex(data: RawItem[][]): ChartDataItem[] { |
|
|
|
if (data.length === 0) return []; |
|
|
|
|
|
|
|
// 找出最大长度(防止越界) |
|
|
|
const maxLength = Math.max(...data.map(group => group.length)); |
|
|
|
|
|
|
|
const result: ChartDataItem[] = []; |
|
|
|
|
|
|
|
for (let i = 0; i < maxLength; i++) { |
|
|
|
let totalValue = 0; |
|
|
|
let timeLabel = ''; |
|
|
|
|
|
|
|
for (const group of data) { |
|
|
|
const item = group[i]; |
|
|
|
if (item) { |
|
|
|
totalValue += item.measureValue; |
|
|
|
// 第一次遇到有效时间就记录(假设所有 group[i].measureTime 相同) |
|
|
|
if (!timeLabel) { |
|
|
|
timeLabel = formatTimestamp(item.measureTime); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 如果这一列完全没有数据,跳过(或根据需求处理) |
|
|
|
if (timeLabel) { |
|
|
|
result.push({ |
|
|
|
name: timeLabel, |
|
|
|
value: totalValue |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
const calculateTotalDiff = (data) => { |
|
|
|
return data.reduce((total, subArray) => { |
|
|
|
if (subArray.length === 0) { |
|
|
|
return total; // 空数组跳过 |
|
|
|
} |
|
|
|
const first = subArray[0].measureValue; |
|
|
|
const last = subArray[subArray.length - 1].measureValue; |
|
|
|
return total + (last - first); |
|
|
|
}, 0); |
|
|
|
} |
|
|
|
|
|
|
|
const handleSelectDate = async (range: string[] | null) => { |
|
|
|
if (!range || range.length !== 2) { |
|
|
|
// 清空逻辑 |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const timestamps = parseRangeToTimestamp(range, historyTimeGranularity.value); |
|
|
|
if (!timestamps) return; |
|
|
|
|
|
|
|
const [start, end] = timestamps; |
|
|
|
let timeTypeId = 0 |
|
|
|
switch (historyTimeGranularity.value) { |
|
|
|
case 'day': |
|
|
|
timeTypeId = 3 |
|
|
|
break |
|
|
|
/* case 'week': |
|
|
|
timeTypeId = 4 |
|
|
|
break */ |
|
|
|
case 'month': |
|
|
|
timeTypeId = 4 |
|
|
|
break |
|
|
|
case 'year': |
|
|
|
timeTypeId = 5 |
|
|
|
break |
|
|
|
default: |
|
|
|
timeTypeId = 0 |
|
|
|
break |
|
|
|
} |
|
|
|
const params = { |
|
|
|
start, |
|
|
|
end, |
|
|
|
timeType: timeTypeId // 或者映射为后端需要的 code |
|
|
|
}; |
|
|
|
console.log(params) |
|
|
|
const res = await MapApi.getByStartAndEndTimeAndTimeType(params) |
|
|
|
const array = Object.values(res); |
|
|
|
if (array.length === 0) { |
|
|
|
historyData.value = { |
|
|
|
totalPower: 0, |
|
|
|
fuel: 0, |
|
|
|
co2: 0, |
|
|
|
pm25: 0, |
|
|
|
nox: 0, |
|
|
|
so2: 0, |
|
|
|
} |
|
|
|
h_fuelReductionChartData.value = [ |
|
|
|
{ name: '', value: 0 } |
|
|
|
] |
|
|
|
h_co2ReductionChartData.value = [ |
|
|
|
{ name: '', value: 0 } |
|
|
|
] |
|
|
|
h_so2ReductionChartData.value = [ |
|
|
|
{ name: '', value: 0 } |
|
|
|
] |
|
|
|
h_noxReductionChartData.value = [ |
|
|
|
{ name: '', value: 0 } |
|
|
|
] |
|
|
|
h_pm25ReductionChartData.value = [ |
|
|
|
{ name: '', value: 0 } |
|
|
|
] |
|
|
|
return; |
|
|
|
} |
|
|
|
console.log(array) |
|
|
|
const totalDiff = calculateTotalDiff(array); |
|
|
|
console.log(totalDiff) |
|
|
|
|
|
|
|
|
|
|
|
historyData.value = { |
|
|
|
totalPower: Number(totalDiff.toFixed(2)), |
|
|
|
fuel: Number((totalDiff * 0.22 / 1).toFixed(2)), // 转化为千克 |
|
|
|
co2: Number((totalDiff * 670 / 1000).toFixed(2)), // 克转化为千克 |
|
|
|
pm25: Number((totalDiff * 1.46 / 1000).toFixed(2)), // 克转化为千克 |
|
|
|
nox: Number((totalDiff * 18.1 / 1000).toFixed(2)), // 克转化为千克 |
|
|
|
so2: Number((totalDiff * 10.5 / 1000).toFixed(2)), // 克转化为千克 |
|
|
|
} |
|
|
|
const chartData = aggregateByIndex(array); |
|
|
|
const fuelChartData = chartData.map(item => ({ name: item.name, value: Number((item.value * 0.22 / 1).toFixed(2)) })) |
|
|
|
h_fuelReductionChartData.value = fuelChartData; |
|
|
|
|
|
|
|
const co2ChartData = chartData.map(item => ({ name: item.name, value: Number((item.value * 670 / 1000).toFixed(2)) })) |
|
|
|
h_co2ReductionChartData.value = co2ChartData; |
|
|
|
|
|
|
|
const pm25ChartData = chartData.map(item => ({ name: item.name, value: Number((item.value * 1.46 / 1000).toFixed(2)) })) |
|
|
|
h_pm25ReductionChartData.value = pm25ChartData; |
|
|
|
|
|
|
|
const noxChartData = chartData.map(item => ({ name: item.name, value: Number((item.value * 18.1 / 1000).toFixed(2)) })) |
|
|
|
h_noxReductionChartData.value = noxChartData; |
|
|
|
|
|
|
|
const so2ChartData = chartData.map(item => ({ name: item.name, value: Number((item.value * 10.5 / 1000).toFixed(2)) })) |
|
|
|
h_so2ReductionChartData.value = so2ChartData; |
|
|
|
}; |
|
|
|
|
|
|
|
// 获取对应卡片的图表数据 |
|
|
|
const getChartData = (cardId: string): ChartDataItem[] => { |
|
|
|
switch (cardId) { |
|
|
|
@ -407,18 +828,37 @@ const getChartData = (cardId: string): ChartDataItem[] => { |
|
|
|
return commonChartData.value |
|
|
|
} |
|
|
|
} |
|
|
|
// 获取对应卡片的图表数据 |
|
|
|
const getHistoryChartData = (cardId: string): ChartDataItem[] => { |
|
|
|
switch (cardId) { |
|
|
|
case 'overview': |
|
|
|
return totalPowerChartData.value |
|
|
|
case 'fuel': |
|
|
|
return h_fuelReductionChartData.value |
|
|
|
case 'co2': |
|
|
|
return h_co2ReductionChartData.value |
|
|
|
case 'pm25': |
|
|
|
return h_pm25ReductionChartData.value |
|
|
|
case 'nox': |
|
|
|
return h_noxReductionChartData.value |
|
|
|
case 'so2': |
|
|
|
return h_so2ReductionChartData.value |
|
|
|
default: |
|
|
|
return commonChartData.value |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 时间范围选项 |
|
|
|
const timeRange = ref<'realtime' | 'day' | 'week' | 'month' | 'quarter' | 'year'>('realtime') |
|
|
|
|
|
|
|
// 时间范围选项定义 |
|
|
|
const timeRangeOptions = [ |
|
|
|
{ value: 'realtime', label: '实时' }, |
|
|
|
{ value: 'day', label: '日' }, |
|
|
|
{ value: 'week', label: '周' }, |
|
|
|
{ value: 'month', label: '月' }, |
|
|
|
{ value: 'realtime', label: '总' }, |
|
|
|
{ value: 'day', label: '本日' }, |
|
|
|
{ value: 'week', label: '本周' }, |
|
|
|
{ value: 'month', label: '本月' }, |
|
|
|
// { value: 'quarter', label: '季' }, |
|
|
|
{ value: 'year', label: '年' } |
|
|
|
{ value: 'year', label: '本年' } |
|
|
|
] |
|
|
|
|
|
|
|
// 处理时间范围选择 |
|
|
|
@ -427,6 +867,30 @@ const handleTimeRangeChange = (range: 'realtime' | 'day' | 'week' | 'month' | 'q |
|
|
|
// 这里可以添加根据时间范围切换数据源的逻辑 |
|
|
|
} |
|
|
|
|
|
|
|
// 历史模式时间颗粒度选项 |
|
|
|
const historyTimeGranularity = ref<'year' | 'month' | 'week' | 'day'>('day') |
|
|
|
|
|
|
|
// 历史模式时间颗粒度选项定义 |
|
|
|
const historyTimeGranularityOptions = [ |
|
|
|
{ value: 'year', label: '年' }, |
|
|
|
{ value: 'month', label: '月' }, |
|
|
|
// { value: 'week', label: '周' }, |
|
|
|
{ value: 'day', label: '日' } |
|
|
|
] |
|
|
|
|
|
|
|
// 处理历史模式时间颗粒度选择 |
|
|
|
const handleHistoryTimeGranularityChange = (granularity: 'year' | 'month' | 'week' | 'day') => { |
|
|
|
historyTimeGranularity.value = granularity |
|
|
|
// 这里可以添加根据时间颗粒度切换数据源的逻辑 |
|
|
|
} |
|
|
|
|
|
|
|
// 处理日期范围确认 |
|
|
|
const handleDateRangeConfirm = () => { |
|
|
|
// 这里可以添加处理日期范围确认的逻辑 |
|
|
|
console.log('日期范围已确认,当前颗粒度:', historyTimeGranularity.value) |
|
|
|
// 可以调用API获取选中日期范围内的数据 |
|
|
|
} |
|
|
|
|
|
|
|
// 定义卡片信息 |
|
|
|
const cards = ref<CardInfo[]>([ |
|
|
|
{ |
|
|
|
@ -524,10 +988,20 @@ const handleGetRealTimeAllData = async (realtimeDeviceData: RealtimeDeviceData[] |
|
|
|
ids: ids.join(','), |
|
|
|
// year: new Date().getFullYear() |
|
|
|
} |
|
|
|
const yearDataRes: RealtimeDeviceData[] = await MapApi.getYearDataByIdList(params); |
|
|
|
const weekDataRes: RealtimeDeviceData[] = await MapApi.getWeekDataByIdList(params); |
|
|
|
const monthDataRes: RealtimeDeviceData[] = await MapApi.getMonthDataByIdList(params); |
|
|
|
const dayDataRes: RealtimeDeviceData[] = await MapApi.getDayDataByIdList(params); |
|
|
|
let yearDataRes: RealtimeDeviceData[] = [] |
|
|
|
let weekDataRes: RealtimeDeviceData[] = [] |
|
|
|
let monthDataRes: RealtimeDeviceData[] = [] |
|
|
|
let dayDataRes: RealtimeDeviceData[] = [] |
|
|
|
|
|
|
|
try { |
|
|
|
yearDataRes = await MapApi.getYearDataByIdList(params); |
|
|
|
weekDataRes = await MapApi.getWeekDataByIdList(params); |
|
|
|
monthDataRes = await MapApi.getMonthDataByIdList(params); |
|
|
|
dayDataRes = await MapApi.getDayDataByIdList(params); |
|
|
|
} catch (error) { |
|
|
|
console.log(error); |
|
|
|
} |
|
|
|
|
|
|
|
// const quarterDataRes: deviceData = await MapApi.getQuarterDataByIdList(params); |
|
|
|
|
|
|
|
const realTimeSum = res.reduce((acc, item) => acc + item.measureValue, 0); |
|
|
|
@ -544,7 +1018,7 @@ const handleGetRealTimeAllData = async (realtimeDeviceData: RealtimeDeviceData[] |
|
|
|
// pm25Reduction.value = (realTimeSum * 1.46 / 1000000).toFixed(2) // 克转化为吨 |
|
|
|
// fuelReduction.value = (realTimeSum * 0.22 / 1000).toFixed(2) // 转化为吨 |
|
|
|
|
|
|
|
const incrementRealTimeSum = Object.values(res).reduce((acc, item) => acc + item.incrementValue, 0); |
|
|
|
const incrementRealTimeSum = Object.values(res).reduce((acc, item) => acc + item.measureValue, 0); |
|
|
|
|
|
|
|
|
|
|
|
chartData.value.realtime = { |
|
|
|
@ -629,26 +1103,20 @@ const handleGetRealTimeAllData = async (realtimeDeviceData: RealtimeDeviceData[] |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const handleGetAllDeviceDataByTimeRange = async () => { |
|
|
|
/* const handleGetAllDeviceDataByTimeRange = async () => { |
|
|
|
try { |
|
|
|
// 当前时间的时间戳(毫秒) |
|
|
|
const nowTimestamp = dayjs().valueOf().toString(); |
|
|
|
|
|
|
|
// 两天前的时间戳(毫秒) |
|
|
|
const twoDaysAgoTimestamp = dayjs().subtract(2, 'day').valueOf().toString(); |
|
|
|
const params = { |
|
|
|
start: twoDaysAgoTimestamp, |
|
|
|
end: nowTimestamp, |
|
|
|
timeType: '3' |
|
|
|
} |
|
|
|
const res = await MapApi.getByStartAndEndTimeAndTimeType(params) |
|
|
|
console.log(res) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (error) { |
|
|
|
console.error(error) |
|
|
|
} |
|
|
|
} |
|
|
|
} */ |
|
|
|
|
|
|
|
watch(() => props.realtimeDeviceData, (newValue) => { |
|
|
|
handleGetRealTimeAllData(newValue) |
|
|
|
@ -656,7 +1124,7 @@ watch(() => props.realtimeDeviceData, (newValue) => { |
|
|
|
|
|
|
|
// 组件挂载时初始化数据和定时器 |
|
|
|
onMounted(() => { |
|
|
|
handleGetAllDeviceDataByTimeRange() |
|
|
|
// handleGetAllDeviceDataByTimeRange() |
|
|
|
handleGetRealTimeAllData(props.realtimeDeviceData) |
|
|
|
}) |
|
|
|
|
|
|
|
|