Browse Source

update

feature
jiangAB 2 weeks ago
parent
commit
387e7afc33
  1. 2
      public/map/components/CompanyShorePower.vue
  2. 21
      public/map/components/ShipShorePower.vue
  3. 13
      public/map/components/ShorePowerUsage.vue
  4. 523
      public/map/components/ShorePowerUsageRate.vue
  5. 153
      public/map/components/ShorePowerUsageSingleData.vue
  6. 98
      public/map/components/ShowData.vue
  7. 63
      public/map/components/cesiumMap.vue
  8. 14
      public/map/components/charts/ComparisonBarChart.vue
  9. 68
      public/map/index.vue
  10. 22
      src/types/shorepower.d.ts

2
public/map/components/CompanyShorePower.vue

@ -418,7 +418,7 @@ const handGetDeviceData = (deviceId: number | number[], range: 'realtime' | 'day
// If deviceId is a single number, get its measure value
const deviceData = deviceDataMap.value.get(deviceId);
const startData = rangData.find(item => item.deviceId === deviceId)
console.log(deviceData, startData)
if (deviceData?.measureValue) {
totalValue = deviceData.measureValue;
}

21
public/map/components/ShipShorePower.vue

@ -17,7 +17,7 @@
<div class="card-content">
<div class="overview-grid">
<div class="overview-item">
<div class="overview-value">{{ shipTotalData.berthingShips }}</div>
<div class="overview-value">{{ shipTotalData.allPortAreaCount }}</div>
<div class="overview-label">在港船舶</div>
</div>
<div class="overview-item">
@ -127,7 +127,7 @@
<div class="detail-item">
<span class="label">名称:</span>
<span class="value">{{ selectedShip.shipBasicInfo.name + '-' + selectedShip.shipBasicInfo.nameEn
}}</span>
}}</span>
</div>
<div class="detail-item">
<span class="label">长度:</span>
@ -169,7 +169,7 @@
<div class="detail-item">
<span class="label">靠泊类型:</span>
<span class="value">{{ getOperationTypeLabel(selectedShip.shorePowerAndShip.type, BERTH_TYPE)
}}</span>
}}</span>
</div>
<div class="detail-item">
<span class="label">靠泊时间:</span>
@ -237,7 +237,7 @@
<el-form-item label="到达港:">
<span>{{ getOperationTypeLabel(selectedShip.applyInfo?.arrivalHarborDistrict,
harborDistrictIdAndNameList)
}}</span>
}}</span>
</el-form-item>
</el-col>
</el-row>
@ -292,7 +292,7 @@
<el-col :span="12">
<el-form-item label="货物类型(装货):">
<span>{{ getOperationTypeLabel(selectedShip.applyInfo?.loadingCargoCategory, CARGO_CATEGORY) || '-'
}}</span>
}}</span>
</el-form-item>
</el-col>
<el-col :span="12">
@ -314,7 +314,7 @@
<el-col :span="12">
<el-form-item label="货物类型(卸货):">
<span>{{ getOperationTypeLabel(selectedShip.applyInfo?.unloadingCargoCategory, CARGO_CATEGORY) || '-'
}}</span>
}}</span>
</el-form-item>
</el-col>
<el-col :span="12">
@ -343,7 +343,7 @@
<el-col :span="12" v-if="selectedShip.applyInfo?.reason !== 0">
<el-form-item label="未使用岸电原因:">
<span>{{ getOperationTypeLabel(selectedShip.applyInfo?.reason, UNUSED_SHORE_POWER_REASON) || '-'
}}</span>
}}</span>
</el-form-item>
</el-col>
</el-row>
@ -535,13 +535,13 @@
<el-col :span="12">
<el-form-item label="船舶长度:">
<span>{{ selectedShip.shipBasicInfo?.length !== undefined ? selectedShip.shipBasicInfo?.length : '-'
}}</span>
}}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="船舶宽度:">
<span>{{ selectedShip.shipBasicInfo?.width !== undefined ? selectedShip.shipBasicInfo?.width : '-'
}}</span>
}}</span>
</el-form-item>
</el-col>
</el-row>
@ -550,7 +550,7 @@
<el-col :span="12">
<el-form-item label="船舶吨位:">
<span>{{ selectedShip.shipBasicInfo?.tonnage !== undefined ? selectedShip.shipBasicInfo?.tonnage : '-'
}}</span>
}}</span>
</el-form-item>
</el-col>
<el-col :span="12">
@ -621,6 +621,7 @@ interface Props {
realtimeDeviceData: RealtimeDeviceData[];
realtimeDeviceDataTime: string;
shipTotalData: {
allPortAreaCount: number;
berthingShips: number;
shorePowerShips: number;
noShorePowerShips: number;

13
public/map/components/ShorePowerUsage.vue

@ -284,6 +284,7 @@ interface Props {
activeHeadGroup?: number;
handleGoBack: () => void;
realtimeDeviceDataTime: string;
initialTimeRange?: string;
}
const props = defineProps<Props>()
@ -753,7 +754,9 @@ const getHistoryChartData = (cardId: string): ChartDataItem[] => {
}
//
const timeRange = ref<'realtime' | 'day' | 'month' | 'quarter' | 'year'>('day')
const timeRange = ref<'realtime' | 'day' | 'month' | 'quarter' | 'year'>(
(props.initialTimeRange as 'realtime' | 'day' | 'month' | 'quarter' | 'year') || 'day'
)
//
const timeRangeOptions = [
@ -1049,6 +1052,14 @@ watch(() => props.activeHeadGroup, (newVal) => {
}
}, { immediate: true })
// initialTimeRange
watch(() => props.initialTimeRange, (newVal) => {
console.log('newVal', newVal)
if (newVal) {
timeRange.value = newVal as 'realtime' | 'day' | 'month' | 'year'
}
})
//
onBeforeUnmount(() => {
if (cardSelectedHandler) {

523
public/map/components/ShorePowerUsageRate.vue

@ -0,0 +1,523 @@
<template>
<div class="shore-power-usage">
<div 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">岸电使用率</span>
</div>
<div class="show-value">
<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>
<!-- <span class="show-value-label">{{timeRangeOptions.find(option => option.value === timeRange)?.label ||
''}}:</span>
<div class="show-value-value">{{ chartData[timeRange][card.value] }}</div> -->
<el-button class="close-btn" size="small" type="text" @click.stop="handleClose()">x</el-button>
</div>
</div>
<div style="display: flex; align-items: center; justify-content: space-between;">
<div class="time-range-item">{{ startTimeDisplay }} {{ realtimeDeviceDataTime }}</div>
<div v-show="timeRange !== 'year'" class="comparison-type-selector">
<button :class="['comparison-type-btn', { active: comparisonType === 'chain' }]"
@click="comparisonType = 'chain'">
环比
</button>
<button :class="['comparison-type-btn', { active: comparisonType === 'yearOnYear' }]"
@click="comparisonType = 'yearOnYear'">
同比
</button>
</div>
</div>
<div v-if="timeRange === 'year'" class="card-content">
<ComparisonBarChart :chartData="getYearlyData" title="年度用电量对比" yAxisName="用电量(kWh)" currentColor="#36A2EB"
previousColor="#FF6384" />
</div>
<div v-if="timeRange === 'month'" class="card-content">
<ComparisonBarChart :chartData="getMonthlyComparisonData"
:title="comparisonType === 'yearOnYear' ? '月度用电量同比对比' : '月度用电量环比对比'" yAxisName="用电量(kWh)"
currentColor="#36A2EB" previousColor="#FF6384" />
</div>
<div class="card-content" v-if="timeRange === 'day'">
<ComparisonBarChart :chartData="getDailyComparisonData"
:title="comparisonType === 'yearOnYear' ? '日用电量同比对比' : '日用电量环比对比'" yAxisName="用电量(kWh)" currentColor="#36A2EB"
previousColor="#FF6384" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
import { MapApi } from "@/api/shorepower/map";
import ComparisonBarChart from './charts/ComparisonBarChart.vue'
import { ComparativeData, RealtimeDeviceData } from '@/types/shorepower';
interface Props {
// realtimeDeviceData: RealtimeDeviceData[];
// activeHeadGroup?: number;
handleClose: () => void;
realtimeDeviceDataTime: string;
comparativeData: ComparativeData;
initialTimeRange?: 'realtime' | 'day' | 'month' | 'year';
}
const props = defineProps<Props>()
export interface MeasureDataRealtimeRespVO {
/**
* 创建时间
*/
createTime: Date;
/**
* 设备编号code
*/
deviceCode: string;
/**
* 设备编号
*/
deviceId: number;
/**
* 设备名称
*/
deviceName: string;
/**
* 设备状态
*/
deviceStatus: number;
/**
* 编号
*/
id: number;
/**
* 增量费用
*/
incrementCost?: number;
/**
* 增量值
*/
incrementValue: number;
/**
* 采集时间
*/
measureTime: Date;
/**
* 采集值
*/
measureValue: number;
}
export interface deviceData {
[key: string]: MeasureDataRealtimeRespVO
}
const getYearlyData = computed(() => {
return [{
name: '本期上期对比',
growthRate: .11,
currentValue: 22,
previousValue: 33,
currentPeriod: '2024',
previousPeriod: '2025',
}]
})
// -
const getDailyComparisonData = computed(() => {
// 使
const mockDailyData = {
chain: { //
name: '本期上期对比',
growthRate: .52, // 5.2%
currentValue: 85.5, // 85.5%
previousValue: 81.3, // 81.3%
currentPeriod: new Date().toISOString().split('T')[0], //
previousPeriod: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString().split('T')[0], //
},
yearOnYear: { //
name: '本期去年同期对比',
growthRate: .128, // 12.8%
currentValue: 85.5, // 85.5%
previousValue: 75.8, // 75.8%
currentPeriod: new Date().toISOString().split('T')[0], //
previousPeriod: new Date(Date.now() - 365 * 24 * 60 * 60 * 1000).toISOString().split('T')[0], //
}
};
return [{
name: mockDailyData[comparisonType.value].name,
growthRate: mockDailyData[comparisonType.value].growthRate,
currentValue: convertPowerUsage(mockDailyData[comparisonType.value].currentValue, selectedCard.value),
previousValue: convertPowerUsage(mockDailyData[comparisonType.value].previousValue, selectedCard.value),
currentPeriod: mockDailyData[comparisonType.value].currentPeriod,
previousPeriod: mockDailyData[comparisonType.value].previousPeriod,
}]
})
// -
const getMonthlyComparisonData = computed(() => {
// 使
const mockMonthlyData = {
chain: { //
name: '本期上期对比',
growthRate: .83, // 8.3%
currentValue: 87.2, // 87.2%
previousValue: 80.5, // 80.5%
currentPeriod: new Date().toISOString().slice(0, 7), // YYYY-MM
previousPeriod: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().slice(0, 7), // YYYY-MM
},
yearOnYear: { //
name: '本期去年同期对比',
growthRate: .156, // 15.6%
currentValue: 87.2, // 87.2%
previousValue: 75.4, // 75.4%
currentPeriod: new Date().toISOString().slice(0, 7), // YYYY-MM
previousPeriod: new Date(Date.now() - 365 * 24 * 60 * 60 * 1000).toISOString().slice(0, 7), //
}
};
return [{
name: mockMonthlyData[comparisonType.value].name,
growthRate: mockMonthlyData[comparisonType.value].growthRate,
currentValue: convertPowerUsage(mockMonthlyData[comparisonType.value].currentValue, selectedCard.value),
previousValue: convertPowerUsage(mockMonthlyData[comparisonType.value].previousValue, selectedCard.value),
currentPeriod: mockMonthlyData[comparisonType.value].currentPeriod,
previousPeriod: mockMonthlyData[comparisonType.value].previousPeriod,
}]
})
//
const startTimeDisplay = computed(() => {
const now = new Date();
let startDate: Date;
switch (timeRange.value) {
case 'day':
//
startDate = new Date(now.getFullYear(), now.getMonth(), now.getDate());
break;
case 'month':
//
startDate = new Date(now.getFullYear(), now.getMonth(), 1);
break;
case 'year':
//
startDate = new Date(now.getFullYear(), 0, 1);
break;
/* case 'realtime':
// 2020
startDate = new Date(2020, 0, 1);
break; */
default:
//
startDate = new Date(now.getFullYear(), now.getMonth(), now.getDate());
}
//
const year = startDate.getFullYear();
const month = String(startDate.getMonth() + 1).padStart(2, '0');
const day = String(startDate.getDate()).padStart(2, '0');
const hours = String(startDate.getHours()).padStart(2, '0');
const minutes = String(startDate.getMinutes()).padStart(2, '0');
const seconds = String(startDate.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
});
const selectedCard = ref<string>('overview')
//
const timeRange = ref<'day' | 'month' | 'year'>(
(props.initialTimeRange === 'realtime' ? 'year' : props.initialTimeRange) || 'day'
)
// chain yearOnYear
const comparisonType = ref<'chain' | 'yearOnYear'>('chain')
//
const timeRangeOptions = [
{ value: 'day', label: '当日' },
{ value: 'month', label: '当月' },
{ value: 'year', label: '当年' },
// { value: 'realtime', label: '' },
]
//
const handleTimeRangeChange = (range: 'day' | 'month' | 'year') => {
timeRange.value = range
//
}
//
const convertPowerUsage = (powerUsage: number, type: string): number => {
const conversionFactors: Record<string, number> = {
totalPower: 1, // 使
fuel: 0.22 / 1, //
co2: 670 / 1000, //
pm25: 1.46 / 1000, //
nox: 18.1 / 1000, //
so2: 10.5 / 1000 //
};
const factor = conversionFactors[type] || 1;
return Number((powerUsage * factor).toFixed(2));
};
/* watch(() => props.realtimeDeviceData, (newValue) => {
handleGetRealTimeAllData(newValue)
}) */
//
//
let cardSelectedHandler: any = null
onMounted(() => {
// handleGetAllDeviceDataByTimeRange()
// handleGetRealTimeAllData(props.realtimeDeviceData)
//
// cardSelectedHandler = (event: CustomEvent) => {
// selectedCard.value = event.detail
// }
// window.addEventListener('cardSelected', cardSelectedHandler)
})
// activeHeadGroup resize
watch(() => props.activeHeadGroup, (newVal) => {
if (newVal === 1) {
// resize DOM
setTimeout(() => {
window.dispatchEvent(new Event('resize'))
}, 100)
}
}, { immediate: true })
// initialTimeRange
watch(() => props.initialTimeRange, (newVal) => {
console.log('newVal', newVal)
if (newVal) {
if (newVal === 'realtime') {
timeRange.value = 'year'
} else {
timeRange.value = newVal as 'day' | 'month' | 'year'
}
}
})
//
onBeforeUnmount(() => {
if (cardSelectedHandler) {
window.removeEventListener('cardSelected', cardSelectedHandler)
}
})
</script>
<style lang="scss" scoped>
.shore-power-usage {
display: flex;
height: 100%;
width: 100%;
gap: 10px;
}
.card .card-content {
overflow: hidden !important;
}
.average {
.overview-value {
font-size: 36px;
}
.overview-label {
font-size: 18px;
}
}
.magnify {
width: 100%;
padding: 12px;
height: calc(100vh - 72px);
position: absolute;
top: 72px;
left: 0px;
display: flex;
// flex-direction: column;
/* background-color: red; */
gap: 8px;
overflow-y: auto;
.big {
height: 100%;
width: calc(55%);
.card {
height: 100%;
}
.card .card-title .title-text {
width: 120px;
}
.overview-value {
font-size: 48px;
}
.overview-label {
font-size: 28px;
}
}
.right-row {
width: calc(45% - 8px);
height: 100%;
display: flex;
flex-direction: column;
flex: 1;
gap: 8px;
// background-color: red;
}
.one-row {
height: 30%;
display: flex;
gap: 8px;
.card .card-title .title-text {
font-size: 14px;
}
.card {
flex: 1;
}
}
}
.card {
padding: 6px;
cursor: pointer;
}
.time-range-selector {
display: flex;
align-items: center;
gap: 8px;
}
.show-value {
font-size: 18px;
font-weight: 600;
font-weight: bold;
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 8px;
color: #FFF;
}
.show-value-label {
font-size: 18px;
font-weight: 600;
font-weight: bold;
}
.show-value-value {
font-size: 20px;
font-weight: 600;
font-weight: bold;
}
.time-range-item {
font-size: 24px;
font-weight: 600;
font-weight: bold;
color: rgba(255, 255, 255, 0.7);
}
.growth-rate {
flex: 1;
text-align: right;
margin-left: 10px;
font-weight: bold;
padding: 2px 8px;
border-radius: 4px;
font-size: 32px;
}
.growth-up {
color: #52C41A;
}
.growth-down {
color: #F5222D;
}
.growth-icon {
margin-right: 4px;
}
.comparison-type-selector {
display: inline-flex;
margin: 0 10px;
width: fit-content;
background: rgba(255, 255, 255, 0.1);
padding: 2px;
// margin-bottom: 8px;
border-radius: 4px;
}
.comparison-type-btn {
border: none;
background: transparent;
color: #ccc;
padding: 4px 8px;
border-radius: 2px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
&.active {
background: #1890ff;
color: white;
}
}
.right {
/* gap: 0 */
}
.close-btn {
width: 24px;
height: 24px;
// border-radius: 50%;
// background-color: #ff4d4f;
color: white;
border: none;
font-weight: bold;
font-size: 16px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
margin-left: 10px;
// margin-bottom: 8px;
}
.close-btn:hover {
color: #f5222d;
}
</style>

153
public/map/components/ShorePowerUsageSingleData.vue

@ -13,6 +13,11 @@
</div>
<div class="show-value">
<div class="time-range-item">{{ startTimeDisplay }} {{ realtimeDeviceDataTime }}</div>
<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>
<span class="show-value-label">{{timeRangeOptions.find(option => option.value === timeRange)?.label ||
''}}:</span>
<div class="show-value-value">{{ chartData[timeRange][card.value] }}</div>
@ -28,52 +33,61 @@
</div>
</div>
<div class="right-row">
<div class="card digital-twin-card--deep-blue">
<div v-if="timeRange === 'year'" class="card digital-twin-card--deep-blue">
<div class="card-title">
<div class="vertical-line"></div>
<img src="@/assets/svgs/data.svg" class="title-icon" />
<span class="title-text">年环比</span>
<span class="growth-rate"
:class="{ 'growth-up': getYearlyData[0].growthRate >= 0, 'growth-down': getYearlyData[0].growthRate < 0 }">
<span class="growth-icon">{{ getYearlyData[0].growthRate >= 0 ? '↑' : '↓' }}</span>
{{ (getYearlyData[0].growthRate * 100).toFixed(1) }}%
</span>
</div>
<div class="card-content">
<ComparisonBarChart :chartData="getYearlyData" title="年度用电量对比" yAxisName="用电量(kWh)" currentColor="#36A2EB"
previousColor="#FF6384" />
</div>
</div>
<div class="card digital-twin-card--deep-blue">
<div v-if="timeRange === 'month'" class="card digital-twin-card--deep-blue">
<div class="card-title">
<div class="vertical-line"></div>
<img src="@/assets/svgs/data.svg" class="title-icon" />
<span class="title-text">月环比</span>
<span class="growth-rate"
:class="{ 'growth-up': getMonthlyData[0].growthRate >= 0, 'growth-down': getMonthlyData[0].growthRate < 0 }">
<span class="growth-icon">{{ getMonthlyData[0].growthRate >= 0 ? '↑' : '↓' }}</span>
{{ (getMonthlyData[0].growthRate * 100).toFixed(1) }}%
</span>
<span class="title-text">月对比</span>
<!-- 同比环比选择按钮 -->
<div class="comparison-type-selector">
<button :class="['comparison-type-btn', { active: comparisonType === 'chain' }]"
@click="comparisonType = 'chain'">
环比
</button>
<button :class="['comparison-type-btn', { active: comparisonType === 'yearOnYear' }]"
@click="comparisonType = 'yearOnYear'">
同比
</button>
</div>
</div>
<div class="card-content">
<ComparisonBarChart :chartData="getMonthlyData" title="月度用电量对比" yAxisName="用电量(kWh)"
<ComparisonBarChart :chartData="getMonthlyComparisonData"
:title="comparisonType === 'yearOnYear' ? '月度用电量同比对比' : '月度用电量环比对比'" yAxisName="用电量(kWh)"
currentColor="#36A2EB" previousColor="#FF6384" />
</div>
</div>
<div class="card digital-twin-card--deep-blue">
<div v-if="timeRange === 'day'" class="card digital-twin-card--deep-blue">
<div class="card-title">
<div class="vertical-line"></div>
<img src="@/assets/svgs/data.svg" class="title-icon" />
<span class="title-text">日环比</span>
<span class="growth-rate"
:class="{ 'growth-up': getDailyData[0].growthRate >= 0, 'growth-down': getDailyData[0].growthRate < 0 }">
<span class="growth-icon">{{ getDailyData[0].growthRate >= 0 ? '↑' : '↓' }}</span>
{{ (getDailyData[0].growthRate * 100).toFixed(1) }}%
</span>
<span class="title-text">日对比</span>
<!-- 同比环比选择按钮 -->
<div class="comparison-type-selector">
<button :class="['comparison-type-btn', { active: comparisonType === 'chain' }]"
@click="comparisonType = 'chain'">
环比
</button>
<button :class="['comparison-type-btn', { active: comparisonType === 'yearOnYear' }]"
@click="comparisonType = 'yearOnYear'">
同比
</button>
</div>
</div>
<div class="card-content">
<ComparisonBarChart :chartData="getDailyData" title="日对比" yAxisName="用电量(kWh)" currentColor="#36A2EB"
previousColor="#FF6384" />
<ComparisonBarChart :chartData="getDailyComparisonData"
:title="comparisonType === 'yearOnYear' ? '日用电量同比对比' : '日用电量环比对比'" yAxisName="用电量(kWh)"
currentColor="#36A2EB" previousColor="#FF6384" />
</div>
</div>
</div>
@ -276,6 +290,7 @@ interface Props {
handleGoBack: () => void;
realtimeDeviceDataTime: string;
comparativeData: ComparativeData;
initialTimeRange?: 'realtime' | 'day' | 'month' | 'year';
}
@ -396,6 +411,38 @@ const getYearlyData = computed(() => {
}]
})
//
const getDailyComparisonData = computed(() => {
const comparisonData = comparisonType.value === 'yearOnYear'
? props.comparativeData.dayYearOnYear
: props.comparativeData.day;
return [{
name: comparisonType.value === 'yearOnYear' ? '本期去年同期对比' : '本期上期对比',
growthRate: comparisonData.growthRate,
currentValue: convertPowerUsage(comparisonData.current.value, selectedCard.value),
previousValue: convertPowerUsage(comparisonData.previous.value, selectedCard.value),
currentPeriod: comparisonData.current.period,
previousPeriod: comparisonData.previous.period,
}]
})
//
const getMonthlyComparisonData = computed(() => {
const comparisonData = comparisonType.value === 'yearOnYear'
? props.comparativeData.monthYearOnYear
: props.comparativeData.month;
return [{
name: comparisonType.value === 'yearOnYear' ? '本期去年同期对比' : '本期上期对比',
growthRate: comparisonData.growthRate,
currentValue: convertPowerUsage(comparisonData.current.value, selectedCard.value),
previousValue: convertPowerUsage(comparisonData.previous.value, selectedCard.value),
currentPeriod: comparisonData.current.period,
previousPeriod: comparisonData.previous.period,
}]
})
//
const startTimeDisplay = computed(() => {
const now = new Date();
@ -516,14 +563,6 @@ const chartData = ref({
'nox': 0,
'so2': 0,
},
'quarter': {
'totalPower': 0,
'fuel': 0,
'co2': 0,
'pm25': 0,
'nox': 0,
'so2': 0,
},
'year': {
'totalPower': 0,
'fuel': 0,
@ -713,18 +752,23 @@ const getHistoryChartData = (cardId: string): ChartDataItem[] => {
}
//
const timeRange = ref<'realtime' | 'day' | 'month' | 'year'>('day')
const timeRange = ref<'day' | 'month' | 'year'>(
(props.initialTimeRange === 'realtime' ? 'year' : props.initialTimeRange) || 'day'
)
// chain yearOnYear
const comparisonType = ref<'chain' | 'yearOnYear'>('chain')
//
const timeRangeOptions = [
{ value: 'day', label: '当日' },
{ value: 'month', label: '当月' },
{ value: 'year', label: '当年' },
{ value: 'realtime', label: '汇总' },
// { value: 'realtime', label: '' },
]
//
const handleTimeRangeChange = (range: 'realtime' | 'day' | 'month' | 'year') => {
const handleTimeRangeChange = (range: 'day' | 'month' | 'year') => {
timeRange.value = range
//
}
@ -1032,6 +1076,18 @@ watch(() => props.activeHeadGroup, (newVal) => {
}
}, { immediate: true })
// initialTimeRange
watch(() => props.initialTimeRange, (newVal) => {
console.log('newVal', newVal)
if (newVal) {
if (newVal === 'realtime') {
timeRange.value = 'year'
} else {
timeRange.value = newVal as 'day' | 'month' | 'year'
}
}
})
//
onBeforeUnmount(() => {
if (cardSelectedHandler) {
@ -1083,6 +1139,11 @@ onBeforeUnmount(() => {
height: 100%;
}
.card .card-title .title-text {
width: 120px;
}
.overview-value {
font-size: 48px;
}
@ -1180,6 +1241,30 @@ onBeforeUnmount(() => {
margin-right: 4px;
}
.comparison-type-selector {
display: inline-flex;
margin: 0 10px;
background: rgba(255, 255, 255, 0.1);
padding: 2px;
border-radius: 4px;
}
.comparison-type-btn {
border: none;
background: transparent;
color: #ccc;
padding: 4px 8px;
border-radius: 2px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
&.active {
background: #1890ff;
color: white;
}
}
.right {
/* gap: 0 */
}

98
public/map/components/ShowData.vue

@ -1,8 +1,8 @@
<template>
<div class="show-data">
<div class="left" v-if="showShipList">
<div class="left">
<!-- <div style="display: flex; align-items: center; justify-content: space-between;"> -->
<div class="card digital-twin-card--deep-blue " style=" display: inline-block; width: 420px;">
<div v-if="showShipList" class="card digital-twin-card--deep-blue " style=" display: inline-block; width: 420px;">
<div style="display: flex; align-items: center; justify-content: space-between;">
<div class="card-title">
<div class="vertical-line"></div>
@ -50,7 +50,7 @@
</div>
</div>
</div>
<div v-if="shipSelectedItem" class="card digital-twin-card--deep-blue"
<div v-if="shipSelectedItem && showShipList" class="card digital-twin-card--deep-blue"
style=" display: inline-block; width: 420px;">
<div class="card-title">
<el-button class="close-btn" size="small" type="text" @click="handleCloseShipDetail">×</el-button>
@ -64,7 +64,7 @@
<span class="label">名称:</span>
<span class="value">{{ shipSelectedItem.shipBasicInfo.name + '-' +
shipSelectedItem.shipBasicInfo.nameEn
}}</span>
}}</span>
</div>
<!-- <div class="detail-item">
<span class="label">英文船名:</span>
@ -118,7 +118,7 @@
<span class="label">靠泊类型:</span>
<span class="value">{{ getOperationTypeLabel(shipSelectedItem.shorePowerAndShip.type,
BERTH_TYPE)
}}</span>
}}</span>
</div>
<div class="detail-item">
<span class="label">靠泊时间:</span>
@ -129,7 +129,7 @@
<span class="label">当前状态:</span>
<span class="value">{{ shipSelectedItem.shipStatus }}<span v-if="shipSelectedItem.StatusReason">({{
shipSelectedItem.StatusReason
}})</span></span>
}})</span></span>
</div>
<div v-if="shipSelectedItem.applyInfo.reason === 0" class="detail-item">
<span class="label">岸电使用时长</span>
@ -147,16 +147,32 @@
<div class="detail-item">
<span class="label">岸电联系人:</span>
<span class="value">{{ shipSelectedItem?.shipBasicInfo?.shorePowerContact
}}</span>
}}</span>
</div>
<div class="detail-item">
<span class="label">联系方式:</span>
<span class="value">{{ shipSelectedItem?.shipBasicInfo?.shorePowerContactPhone
}}</span>
}}</span>
</div>
</div>
</div>
</div>
<div v-if="shorePowerCardShow" style="width: 840px">
<ShorePowerUsageRate :realtimeDeviceDataTime="realtimeDeviceDataTime" :handleClose="handleCloseRate" />
</div>
<!-- <div v-if="shorePowerCardShow" class="card digital-twin-card--deep-blue"
style=" display: inline-block; width: 840px;">
<div class="card-title">
<el-button class="close-btn" size="small" type="text" @click="handleCloseShipDetail">×</el-button>
<div class="vertical-line"></div>
<img src="@/assets/svgs/data.svg" class="title-icon" />
<span class="title-text">岸电使用率</span>
</div>
<div class="card-content">
12
</div>
</div> -->
<!-- </div> -->
</div>
<div class="right" style="width: 900px">
@ -173,7 +189,7 @@
<div class="module-header">
<div style="display: flex; align-items: center; gap: 20px;">
<h3 class="module-title" @click="handleGoToModule(1)">港区岸电用电量统计</h3>
<h3 class="module-title" @click="handleGoToModule(1, portActivePeriod)">港区岸电用电量统计</h3>
<div class="module-time">
<div>{{ portStartTime }} {{ realtimeDeviceDataTime }}</div>
</div>
@ -275,7 +291,7 @@
</div>
<!-- 船舶岸电使用情况 -->
<div class="module-section">
<div class="module-section" style="flex: 1;">
<div class="module-header">
<div style="display: flex; align-items: center; gap: 20px;">
<h3 class="module-title" @click="handleGoToModule(3)">港区船舶岸电用电量统计</h3>
@ -293,13 +309,13 @@
</div>
</div>
<div class="ship-status">
<div class="usage-rate shadow">
<div class="usage-rate shadow" @click="handleOpenRate">
<div class="rate-value">{{ calculateShorePowerUsageRate() }}%</div>
<div class="rate-label">岸电使用率</div>
</div>
<div class="status-grid">
<div class="status-card" @click="handleShowShipList('all')">
<div class="status-value">{{ shipTotalData.berthingShips }}</div>
<div class="status-card">
<div class="status-value">{{ shipTotalData.allPortAreaCount }}</div>
<div class="status-label">在港船舶</div>
</div>
<div class="status-card" @click="handleShowShipList('all')">
@ -371,10 +387,11 @@ import ShipHistoryDialog from './ShipHistoryDialog.vue'
import { formatTimestamp, getValueById, showStatus } from './utils';
import { BERTH_TYPE, getOperationTypeLabel, SHORE_POWER_STATUS, UNUSED_SHORE_POWER_REASON } from './dictionaryTable';
interface Props {
handleGoToModule: (moduleType: number) => void
handleGoToModule: (moduleType: number, timeRange?: string) => void
realtimeDeviceDataTime: string
realtimeDeviceData: RealtimeDeviceData[]
companyShorePowerBuildData: CompanyShorePowerBuildDataItem
companyShorePowerBuildData: CompanyShorePowerBuildDataItem[]
allPortAreaCount: number
chartData: {
[key in 'day' | 'month' | 'year' | 'realtime']: {
'totalPower': number,
@ -386,6 +403,7 @@ interface Props {
}
};
shipTotalData: {
'allPortAreaCount': number,
'berthingShips': number,
'shorePowerShips': number,
'noShorePowerShips': number,
@ -431,6 +449,7 @@ const shipHistoryVisible = ref({
})
const shipSelectedItem = ref<ShipRespVo | null>(null)
const shorePowerCardShow = ref(true)
const periodOptions = [
{ label: '当日', value: 'day' },
@ -463,6 +482,18 @@ const rankingData = {
]
}
// 使
const handleCloseRate = () => {
shorePowerCardShow.value = false
}
const handleOpenRate = () => {
handleCloseShipList()
shorePowerCardShow.value = true
}
// -
const portStartTime = computed(() => {
const now = new Date()
@ -576,7 +607,7 @@ const calculateShorePowerUsageRate = (): number => {
if (!props.shipTotalData || props.shipTotalData.berthingShips === 0) return 0
// 使
const usageRate = (props.shipTotalData.shorePowerShips / props.shipTotalData.berthingShips) * 100
const usageRate = (props.shipTotalData.shorePowerShips / props.shipTotalData.allPortAreaCount) * 100
return Number(usageRate.toFixed(2))
}
@ -613,7 +644,7 @@ const switchShipPeriod = (period: string) => {
//
const handleCardClick = (cardType: string) => {
// 使activeHeadGroup = 1
props.handleGoToModule(5)
props.handleGoToModule(5, portActivePeriod.value)
// 使nextTickselectedCard
setTimeout(() => {
// 线ShorePowerUsage
@ -712,6 +743,7 @@ const showShipHistory = (ship: ShipRespVo) => {
}
const handleShowShipList = (type: string) => {
handleCloseRate()
filterType.value = type
showShipList.value = true
}
@ -808,7 +840,7 @@ const handleCloseShipDetail = () => {
}
.module-section {
// margin-bottom: 8px;
margin-bottom: 8px;
}
.module-header {
@ -854,20 +886,20 @@ const handleCloseShipDetail = () => {
/* 港口岸电使用情况 */
.port-overview {
margin-bottom: 6px;
margin-bottom: 6px 12px;
}
.first-row {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 4px;
margin-bottom: 4px;
gap: 12px;
margin-bottom: 12px;
}
.second-row {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 4px;
gap: 12px;
}
.total-electricity {
@ -984,14 +1016,27 @@ const handleCloseShipDetail = () => {
/* 船舶岸电使用情况 */
.ship-status {
margin-bottom: 8px;
height: 100%;
}
.usage-rate {
text-align: center;
height: 50%;
padding: 16px 8px;
background: rgba(30, 120, 255, 0.15);
border-radius: 10px;
margin-bottom: 6px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
color: #66b1ff;
}
}
.usage-rate.shadow {
@ -1012,10 +1057,11 @@ const handleCloseShipDetail = () => {
}
.status-grid {
height: 38%;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 6px;
margin-bottom: 6px;
gap: 12px;
margin-bottom: 12px;
}
.status-card {
@ -1026,6 +1072,10 @@ const handleCloseShipDetail = () => {
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.status-card:hover {

63
public/map/components/cesiumMap.vue

@ -342,7 +342,10 @@ onMounted(async () => {
const labelInstance = []
const oneTitleInstance = []
const twoTitleInstance = []
const tempShorePowerInstance = []
const ShorePowerAndShipInstance = []
shorePowerList.forEach((item) => {
console.log(item.id)
const itemShipInfo = dataInfo.filter(mapitem => (item.id === mapitem.parentId) && mapitem.type === 5)
const result = itemShipInfo
.filter(item => {
@ -387,6 +390,7 @@ onMounted(async () => {
backFaceCulling: true
},
});
tempShorePowerInstance.push({ storePowerId: item.id, storePowerInstance: xelectricalBoxModel })
if (['国投-206#泊位-高压', '国投-207#泊位-高压', '国投-208#泊位-高压'].includes(item.name)) {
const status = item.storePowerStatus == '在⽤' ? 1 : 0
arr.arr1.push({ xelectricalBoxModel, status })
@ -480,7 +484,7 @@ onMounted(async () => {
labelInstance.push(xelectricalBoxLabel)
})
shipData.forEach(item => {
console.log(item.shorePower.id)
const itemShipInfo = dataInfo.filter(shipItem => (item.shorePower.id === shipItem.parentId) && shipItem.type === 5)
const result = itemShipInfo
.filter(item => {
@ -525,6 +529,15 @@ onMounted(async () => {
backFaceCulling: true
},
});
const foundShorePower = tempShorePowerInstance.find(tempShorePowerItem => tempShorePowerItem.storePowerId === item.shorePower.id);
if (foundShorePower) {
ShorePowerAndShipInstance.push({
...foundShorePower,
shipInstance: xelectricalBoxModel,
shipId: item.shipBasicInfo.id,
status: item.shipStatus
});
}
modelInstance.push({
model: xelectricalBoxModel,
type: 'ship',
@ -659,10 +672,12 @@ onMounted(async () => {
modelInstance,
labelInstance,
oneTitleInstance,
twoTitleInstance
twoTitleInstance,
ShorePowerAndShipInstance
}
}
const { modelInstance, labelInstance, oneTitleInstance, twoTitleInstance } = createMarker()
const { modelInstance, labelInstance, oneTitleInstance, twoTitleInstance, ShorePowerAndShipInstance } = createMarker()
console.log('ShorePowerAndShipInstance', ShorePowerAndShipInstance)
globalModelInstance = modelInstance
// const xitemShipInfo = shipData.find(shipItem => (shipItem.shorePower.id === item.parentId) && item.type === 5)
const tempExe = [
@ -683,6 +698,46 @@ onMounted(async () => {
gradient: 1 //
});
}
function createShipAndShorePowerLine() {
ShorePowerAndShipInstance.forEach((entityItem, idx) => {
console.log('entityItem', entityItem)
const shipEntity = entityItem.shipInstance
const shorePowerEntity = entityItem.storePowerInstance
// position
if (!shipEntity || !shorePowerEntity || !shipEntity.position || !shorePowerEntity.position) {
console.warn('Missing entity or position property:', entityItem);
return;
}
if (entityItem.status !== '在用') return;
const lineEntity = viewer.entities.add({
name: `ship_shorePower_line_${idx}`,
polyline: {
positions: new Cesium.CallbackProperty(() => {
try {
//
const mainPosition = shorePowerEntity.position.getValue(viewer.clock.currentTime);
//
const targetPosition = shipEntity.position.getValue(viewer.clock.currentTime);
//
if (!mainPosition || !targetPosition) {
return [];
}
//
return [mainPosition, targetPosition];
} catch (error) {
console.error('Error getting entity positions:', error);
return [];
}
}, false),
width: 3,
material: createFlowMaterial(entityItem.status === '在用' ? 1 : 0),
// 使线
enableDepthTest: true
}
});
});
}
createShipAndShorePowerLine()
function tempCreate(item) {
tempExe.forEach((exeItem, index) => {
const wgsCoords = coordtransform.wgs84togcj02(exeItem.lon, exeItem.lat);
@ -775,7 +830,7 @@ onMounted(async () => {
return [mainPosition, targetPosition];
}, false),
width: 3,
material: createFlowMaterial(entityItem.status),
material: createFlowMaterial(0),
// 使线
enableDepthTest: true
}

14
public/map/components/charts/ComparisonBarChart.vue

@ -97,7 +97,19 @@ const createBarOption = (currentLabel: string, previousLabel: string, currentVal
return params.value > 0 ? params.value.toFixed(0) : '';
}
}
}]
}],
//
title: props.chartData[0]?.growthRate !== undefined ? {
text: `${props.chartData[0].growthRate >= 0 ? '↑' : '↓'} ${(props.chartData[0].growthRate * 100).toFixed(1)}%`,
left: 'center',
top: '5%',
textStyle: {
fontSize: 32,
fontWeight: 'bold',
color: props.chartData[0].growthRate >= 0 ? '#52c41a' : '#f5222d',
fontFamily: 'Arial, sans-serif'
}
} : undefined
};
}

68
public/map/index.vue

@ -39,13 +39,14 @@
<!-- 港口岸电使用情况多数据 -->
<ShorePowerUsage v-show="activeHeadGroup === 1" :realtime-device-data-time="realtimeDeviceDataTime"
:realtime-device-data="realtimeDeviceData" :active-head-group="activeHeadGroup" v-if="dataLoad"
:handleGoBack="handleGoBack" />
:realtime-device-data="realtimeDeviceData" :active-head-group="activeHeadGroup"
:initial-time-range="selectedTimeRange" v-if="dataLoad" :handleGoBack="handleGoBack" />
<!-- 港口岸电使用情况单数据 -->
<ShorePowerUsageSingleData v-if="activeHeadGroup === 5 && dataLoad"
:realtime-device-data-time="realtimeDeviceDataTime" :realtime-device-data="realtimeDeviceData"
:active-head-group="activeHeadGroup" :handleGoBack="handleGoBack" :comparative-data="comparativeData" />
:active-head-group="activeHeadGroup" :initial-time-range="selectedTimeRange" :handleGoBack="handleGoBack"
:comparative-data="comparativeData" />
<!-- 港口企业岸电使用 -->
<!-- <template v-if="activeHeadGroup === 2"> -->
@ -283,6 +284,7 @@ const headGroupList = ref<{ value: number, label: string }[]>([
])
const activeHeadGroup = ref<number>(0)
const selectedTimeRange = ref<string>('day')
//
interface PublicMapComponentsType {
@ -325,6 +327,10 @@ const yearDataRes = ref<any>({})
const monthDataRes = ref<any>({})
const dayDataRes = ref<any>({})
// const allPortAreaCount = ref<number>(0)
const comparativeData = ref<ComparativeData>({})
@ -386,6 +392,7 @@ const chartData = ref({
const companyShorePowerBuildData = ref<CompanyShorePowerBuildDataItem | null>(null)
const shipTotalData = ref<any>({
'allPortAreaCount': 0,
'berthingShips': 0,
'shorePowerShips': 0,
'noShorePowerShips': 0,
@ -713,8 +720,6 @@ onMounted(async () => {
yearDataRes.value = await MapApi.getYearDataByIdList(params);
monthDataRes.value = await MapApi.getMonthDataByIdList(params);
dayDataRes.value = await MapApi.getDayDataByIdList(params);
const applyShipCount = await MapApi.getApplyShipCount()
console.log('applyShipCount', applyShipCount)
handleGetlRangeData(firstData)
handleBuildCompanyShorePower(firstData)
@ -811,8 +816,16 @@ const shorePowerStatusData = computed(() => {
})
})
const handleGoToModule = (moduleType: number) => {
const handleGoToModule = (moduleType: number, timeRange?: string) => {
activeHeadGroup.value = moduleType
// 1使
if ([1, 5].includes(moduleType) && timeRange) {
// ShorePowerUsage
console.log('timeRange', timeRange)
// ShorePowerUsage
selectedTimeRange.value = timeRange
}
}
const handleGoBack = () => {
activeHeadGroup.value = 0
@ -1058,18 +1071,22 @@ const handleBuildCompanyShorePower = (realtimeDeviceData: RealtimeDeviceData[])
// console.log(buildData);
}
const handleBuildCompanyShorePowerYear = (shipData: any) => {
const handleBuildCompanyShorePowerYear = async (shipData: any) => {
const applyShipCount = await MapApi.getApplyShipCount() as number
shipTotalData.value = {
'allPortAreaCount': applyShipCount,
'berthingShips': shipData.length,
'shorePowerShips': shipData.filter(ship => ['在用'].includes(ship.shipStatus || '')).length,
'noShorePowerShips': shipData.filter(ship => !['在用'].includes(ship.shipStatus || '')).length,
'noShorePowerShips': applyShipCount - shipData.filter(ship => ['在用'].includes(ship.shipStatus || '')).length,
}
}
/**
* 构建日年三个维度的环比对比数据
* 构建日年三个维度的环比对比数据和同比数据
* - 日环比今日 00:00 至当前 vs 昨日 00:00 至昨日此时
* - 日同比今日 00:00 至当前 vs 去年今日 00:00 至去年此时
* - 月环比本月 01 日至当前 vs 上月 01 日至上月同日对齐天数
* - 月同比本月 01 日至当前 vs 去年同月 01 日至去年同日对齐天数
* - 年环比本年 01-01 至当前 vs 去年 01-01 至去年同日对齐天数
*/
const handleBuildTimeComparison = async (): Promise<ComparativeData> => {
@ -1107,18 +1124,31 @@ const handleBuildTimeComparison = async (): Promise<ComparativeData> => {
const yesterdayStart = yesterday.startOf('day').valueOf();
const yesterdayEnd = yesterday.valueOf();
// ===== =====
const lastYearToday = now.subtract(1, 'year');
const lastYearTodayStart = lastYearToday.startOf('day').valueOf();
const lastYearTodayEnd = lastYearToday.valueOf();
// ===== =====
const thisMonthStart = now.startOf('month').valueOf();
const thisMonthEnd = now.valueOf();
const lastMonth = now.subtract(1, 'month');
const lastMonthStart = lastMonth.startOf('month').valueOf();
// 3130
// ""3130
const daysInLastMonth = lastMonth.daysInMonth();
const todayDate = now.date();
const alignedDay = Math.min(todayDate, daysInLastMonth);
const lastMonthEnd = lastMonth.date(alignedDay).endOf('day').valueOf();
// ===== =====
const lastYearMonth = now.subtract(1, 'year');
const lastYearMonthStart = lastYearMonth.startOf('month').valueOf();
// /
const daysInLastYearMonth = lastYearMonth.daysInMonth();
const lastYearAlignedDay = Math.min(todayDate, daysInLastYearMonth);
const lastYearMonthEnd = lastYearMonth.date(lastYearAlignedDay).endOf('day').valueOf();
// ===== =====
const thisYearStart = now.startOf('year').valueOf();
const thisYearEnd = now.valueOf();
@ -1135,19 +1165,23 @@ const handleBuildTimeComparison = async (): Promise<ComparativeData> => {
lastYearEnd = lastYear.month(now.month()).endOf('month').valueOf();
}
// ===== 6 =====
// ===== 8 =====
const [
todayRes,
yesterdayRes,
lastYearTodayRes,
thisMonthRes,
lastMonthRes,
lastYearMonthRes,
thisYearRes,
lastYearRes
] = await Promise.all([
MapApi.getByStartAndEndTimeAndTimeType({ start: todayStart, end: todayEnd, timeType: 2 }),
MapApi.getByStartAndEndTimeAndTimeType({ start: yesterdayStart, end: yesterdayEnd, timeType: 2 }),
MapApi.getByStartAndEndTimeAndTimeType({ start: lastYearTodayStart, end: lastYearTodayEnd, timeType: 2 }),
MapApi.getByStartAndEndTimeAndTimeType({ start: thisMonthStart, end: thisMonthEnd, timeType: 3 }),
MapApi.getByStartAndEndTimeAndTimeType({ start: lastMonthStart, end: lastMonthEnd, timeType: 3 }),
MapApi.getByStartAndEndTimeAndTimeType({ start: lastYearMonthStart, end: lastYearMonthEnd, timeType: 3 }),
MapApi.getByStartAndEndTimeAndTimeType({ start: thisYearStart, end: thisYearEnd, timeType: 4 }),
MapApi.getByStartAndEndTimeAndTimeType({ start: lastYearStart, end: lastYearEnd, timeType: 4 })
]);
@ -1155,8 +1189,10 @@ const handleBuildTimeComparison = async (): Promise<ComparativeData> => {
// ===== =====
const todayUsage = calculatePeriodUsage(todayRes);
const yesterdayUsage = calculatePeriodUsage(yesterdayRes);
const lastYearTodayUsage = calculatePeriodUsage(lastYearTodayRes);
const thisMonthUsage = calculatePeriodUsage(thisMonthRes);
const lastMonthUsage = calculatePeriodUsage(lastMonthRes);
const lastYearMonthUsage = calculatePeriodUsage(lastYearMonthRes);
const thisYearUsage = calculatePeriodUsage(thisYearRes);
const lastYearUsage = calculatePeriodUsage(lastYearRes);
@ -1167,11 +1203,21 @@ const handleBuildTimeComparison = async (): Promise<ComparativeData> => {
current: { period: now.format('YYYY-MM-DD'), value: todayUsage },
previous: { period: yesterday.format('YYYY-MM-DD'), value: yesterdayUsage }
},
dayYearOnYear: {
growthRate: calculateGrowthRate(todayUsage, lastYearTodayUsage),
current: { period: now.format('YYYY-MM-DD'), value: todayUsage },
previous: { period: lastYearToday.format('YYYY-MM-DD'), value: lastYearTodayUsage }
},
month: {
growthRate: calculateGrowthRate(thisMonthUsage, lastMonthUsage),
current: { period: now.format('YYYY-MM'), value: thisMonthUsage },
previous: { period: lastMonth.format('YYYY-MM'), value: lastMonthUsage }
},
monthYearOnYear: {
growthRate: calculateGrowthRate(thisMonthUsage, lastYearMonthUsage),
current: { period: now.format('YYYY-MM'), value: thisMonthUsage },
previous: { period: lastYearMonth.format('YYYY-MM'), value: lastYearMonthUsage }
},
year: {
growthRate: calculateGrowthRate(thisYearUsage, lastYearUsage),
current: { period: now.format('YYYY'), value: thisYearUsage },

22
src/types/shorepower.d.ts

@ -506,6 +506,17 @@ interface ComparativeData {
value: number
}
}
dayYearOnYear: {
growthRate: number
current: {
period: string
value: number
}
previous: {
period: string
value: number
}
}
month: {
growthRate: number
current: {
@ -517,6 +528,17 @@ interface ComparativeData {
value: number
}
}
monthYearOnYear: {
growthRate: number
current: {
period: string
value: number
}
previous: {
period: string
value: number
}
}
year: {
growthRate: number
current: {

Loading…
Cancel
Save