Browse Source

update

feature
jiangAB 2 weeks ago
parent
commit
387e7afc33
  1. 2
      public/map/components/CompanyShorePower.vue
  2. 3
      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. 88
      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 // If deviceId is a single number, get its measure value
const deviceData = deviceDataMap.value.get(deviceId); const deviceData = deviceDataMap.value.get(deviceId);
const startData = rangData.find(item => item.deviceId === deviceId) const startData = rangData.find(item => item.deviceId === deviceId)
console.log(deviceData, startData)
if (deviceData?.measureValue) { if (deviceData?.measureValue) {
totalValue = deviceData.measureValue; totalValue = deviceData.measureValue;
} }

3
public/map/components/ShipShorePower.vue

@ -17,7 +17,7 @@
<div class="card-content"> <div class="card-content">
<div class="overview-grid"> <div class="overview-grid">
<div class="overview-item"> <div class="overview-item">
<div class="overview-value">{{ shipTotalData.berthingShips }}</div> <div class="overview-value">{{ shipTotalData.allPortAreaCount }}</div>
<div class="overview-label">在港船舶</div> <div class="overview-label">在港船舶</div>
</div> </div>
<div class="overview-item"> <div class="overview-item">
@ -621,6 +621,7 @@ interface Props {
realtimeDeviceData: RealtimeDeviceData[]; realtimeDeviceData: RealtimeDeviceData[];
realtimeDeviceDataTime: string; realtimeDeviceDataTime: string;
shipTotalData: { shipTotalData: {
allPortAreaCount: number;
berthingShips: number; berthingShips: number;
shorePowerShips: number; shorePowerShips: number;
noShorePowerShips: number; noShorePowerShips: number;

13
public/map/components/ShorePowerUsage.vue

@ -284,6 +284,7 @@ interface Props {
activeHeadGroup?: number; activeHeadGroup?: number;
handleGoBack: () => void; handleGoBack: () => void;
realtimeDeviceDataTime: string; realtimeDeviceDataTime: string;
initialTimeRange?: string;
} }
const props = defineProps<Props>() 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 = [ const timeRangeOptions = [
@ -1049,6 +1052,14 @@ watch(() => props.activeHeadGroup, (newVal) => {
} }
}, { immediate: true }) }, { immediate: true })
// initialTimeRange
watch(() => props.initialTimeRange, (newVal) => {
console.log('newVal', newVal)
if (newVal) {
timeRange.value = newVal as 'realtime' | 'day' | 'month' | 'year'
}
})
// //
onBeforeUnmount(() => { onBeforeUnmount(() => {
if (cardSelectedHandler) { 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>
<div class="show-value"> <div class="show-value">
<div class="time-range-item">{{ startTimeDisplay }} {{ realtimeDeviceDataTime }}</div> <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 class="show-value-label">{{timeRangeOptions.find(option => option.value === timeRange)?.label ||
''}}:</span> ''}}:</span>
<div class="show-value-value">{{ chartData[timeRange][card.value] }}</div> <div class="show-value-value">{{ chartData[timeRange][card.value] }}</div>
@ -28,52 +33,61 @@
</div> </div>
</div> </div>
<div class="right-row"> <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="card-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
<img src="@/assets/svgs/data.svg" class="title-icon" /> <img src="@/assets/svgs/data.svg" class="title-icon" />
<span class="title-text">年环比</span> <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>
<div class="card-content"> <div class="card-content">
<ComparisonBarChart :chartData="getYearlyData" title="年度用电量对比" yAxisName="用电量(kWh)" currentColor="#36A2EB" <ComparisonBarChart :chartData="getYearlyData" title="年度用电量对比" yAxisName="用电量(kWh)" currentColor="#36A2EB"
previousColor="#FF6384" /> previousColor="#FF6384" />
</div> </div>
</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="card-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
<img src="@/assets/svgs/data.svg" class="title-icon" /> <img src="@/assets/svgs/data.svg" class="title-icon" />
<span class="title-text">月环比</span> <span class="title-text">月对比</span>
<span class="growth-rate" <!-- 同比环比选择按钮 -->
:class="{ 'growth-up': getMonthlyData[0].growthRate >= 0, 'growth-down': getMonthlyData[0].growthRate < 0 }"> <div class="comparison-type-selector">
<span class="growth-icon">{{ getMonthlyData[0].growthRate >= 0 ? '↑' : '↓' }}</span> <button :class="['comparison-type-btn', { active: comparisonType === 'chain' }]"
{{ (getMonthlyData[0].growthRate * 100).toFixed(1) }}% @click="comparisonType = 'chain'">
</span> 环比
</button>
<button :class="['comparison-type-btn', { active: comparisonType === 'yearOnYear' }]"
@click="comparisonType = 'yearOnYear'">
同比
</button>
</div>
</div> </div>
<div class="card-content"> <div class="card-content">
<ComparisonBarChart :chartData="getMonthlyData" title="月度用电量对比" yAxisName="用电量(kWh)" <ComparisonBarChart :chartData="getMonthlyComparisonData"
:title="comparisonType === 'yearOnYear' ? '月度用电量同比对比' : '月度用电量环比对比'" yAxisName="用电量(kWh)"
currentColor="#36A2EB" previousColor="#FF6384" /> currentColor="#36A2EB" previousColor="#FF6384" />
</div> </div>
</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="card-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
<img src="@/assets/svgs/data.svg" class="title-icon" /> <img src="@/assets/svgs/data.svg" class="title-icon" />
<span class="title-text">日环比</span> <span class="title-text">日对比</span>
<span class="growth-rate" <!-- 同比环比选择按钮 -->
:class="{ 'growth-up': getDailyData[0].growthRate >= 0, 'growth-down': getDailyData[0].growthRate < 0 }"> <div class="comparison-type-selector">
<span class="growth-icon">{{ getDailyData[0].growthRate >= 0 ? '↑' : '↓' }}</span> <button :class="['comparison-type-btn', { active: comparisonType === 'chain' }]"
{{ (getDailyData[0].growthRate * 100).toFixed(1) }}% @click="comparisonType = 'chain'">
</span> 环比
</button>
<button :class="['comparison-type-btn', { active: comparisonType === 'yearOnYear' }]"
@click="comparisonType = 'yearOnYear'">
同比
</button>
</div>
</div> </div>
<div class="card-content"> <div class="card-content">
<ComparisonBarChart :chartData="getDailyData" title="日对比" yAxisName="用电量(kWh)" currentColor="#36A2EB" <ComparisonBarChart :chartData="getDailyComparisonData"
previousColor="#FF6384" /> :title="comparisonType === 'yearOnYear' ? '日用电量同比对比' : '日用电量环比对比'" yAxisName="用电量(kWh)"
currentColor="#36A2EB" previousColor="#FF6384" />
</div> </div>
</div> </div>
</div> </div>
@ -276,6 +290,7 @@ interface Props {
handleGoBack: () => void; handleGoBack: () => void;
realtimeDeviceDataTime: string; realtimeDeviceDataTime: string;
comparativeData: ComparativeData; 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 startTimeDisplay = computed(() => {
const now = new Date(); const now = new Date();
@ -516,14 +563,6 @@ const chartData = ref({
'nox': 0, 'nox': 0,
'so2': 0, 'so2': 0,
}, },
'quarter': {
'totalPower': 0,
'fuel': 0,
'co2': 0,
'pm25': 0,
'nox': 0,
'so2': 0,
},
'year': { 'year': {
'totalPower': 0, 'totalPower': 0,
'fuel': 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 = [ const timeRangeOptions = [
{ value: 'day', label: '当日' }, { value: 'day', label: '当日' },
{ value: 'month', label: '当月' }, { value: 'month', label: '当月' },
{ value: 'year', 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 timeRange.value = range
// //
} }
@ -1032,6 +1076,18 @@ watch(() => props.activeHeadGroup, (newVal) => {
} }
}, { immediate: true }) }, { 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(() => { onBeforeUnmount(() => {
if (cardSelectedHandler) { if (cardSelectedHandler) {
@ -1083,6 +1139,11 @@ onBeforeUnmount(() => {
height: 100%; height: 100%;
} }
.card .card-title .title-text {
width: 120px;
}
.overview-value { .overview-value {
font-size: 48px; font-size: 48px;
} }
@ -1180,6 +1241,30 @@ onBeforeUnmount(() => {
margin-right: 4px; 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 { .right {
/* gap: 0 */ /* gap: 0 */
} }

88
public/map/components/ShowData.vue

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

63
public/map/components/cesiumMap.vue

@ -342,7 +342,10 @@ onMounted(async () => {
const labelInstance = [] const labelInstance = []
const oneTitleInstance = [] const oneTitleInstance = []
const twoTitleInstance = [] const twoTitleInstance = []
const tempShorePowerInstance = []
const ShorePowerAndShipInstance = []
shorePowerList.forEach((item) => { shorePowerList.forEach((item) => {
console.log(item.id)
const itemShipInfo = dataInfo.filter(mapitem => (item.id === mapitem.parentId) && mapitem.type === 5) const itemShipInfo = dataInfo.filter(mapitem => (item.id === mapitem.parentId) && mapitem.type === 5)
const result = itemShipInfo const result = itemShipInfo
.filter(item => { .filter(item => {
@ -387,6 +390,7 @@ onMounted(async () => {
backFaceCulling: true backFaceCulling: true
}, },
}); });
tempShorePowerInstance.push({ storePowerId: item.id, storePowerInstance: xelectricalBoxModel })
if (['国投-206#泊位-高压', '国投-207#泊位-高压', '国投-208#泊位-高压'].includes(item.name)) { if (['国投-206#泊位-高压', '国投-207#泊位-高压', '国投-208#泊位-高压'].includes(item.name)) {
const status = item.storePowerStatus == '在⽤' ? 1 : 0 const status = item.storePowerStatus == '在⽤' ? 1 : 0
arr.arr1.push({ xelectricalBoxModel, status }) arr.arr1.push({ xelectricalBoxModel, status })
@ -480,7 +484,7 @@ onMounted(async () => {
labelInstance.push(xelectricalBoxLabel) labelInstance.push(xelectricalBoxLabel)
}) })
shipData.forEach(item => { shipData.forEach(item => {
console.log(item.shorePower.id)
const itemShipInfo = dataInfo.filter(shipItem => (item.shorePower.id === shipItem.parentId) && shipItem.type === 5) const itemShipInfo = dataInfo.filter(shipItem => (item.shorePower.id === shipItem.parentId) && shipItem.type === 5)
const result = itemShipInfo const result = itemShipInfo
.filter(item => { .filter(item => {
@ -525,6 +529,15 @@ onMounted(async () => {
backFaceCulling: true 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({ modelInstance.push({
model: xelectricalBoxModel, model: xelectricalBoxModel,
type: 'ship', type: 'ship',
@ -659,10 +672,12 @@ onMounted(async () => {
modelInstance, modelInstance,
labelInstance, labelInstance,
oneTitleInstance, oneTitleInstance,
twoTitleInstance twoTitleInstance,
ShorePowerAndShipInstance
} }
} }
const { modelInstance, labelInstance, oneTitleInstance, twoTitleInstance } = createMarker() const { modelInstance, labelInstance, oneTitleInstance, twoTitleInstance, ShorePowerAndShipInstance } = createMarker()
console.log('ShorePowerAndShipInstance', ShorePowerAndShipInstance)
globalModelInstance = modelInstance globalModelInstance = modelInstance
// const xitemShipInfo = shipData.find(shipItem => (shipItem.shorePower.id === item.parentId) && item.type === 5) // const xitemShipInfo = shipData.find(shipItem => (shipItem.shorePower.id === item.parentId) && item.type === 5)
const tempExe = [ const tempExe = [
@ -683,6 +698,46 @@ onMounted(async () => {
gradient: 1 // 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) { function tempCreate(item) {
tempExe.forEach((exeItem, index) => { tempExe.forEach((exeItem, index) => {
const wgsCoords = coordtransform.wgs84togcj02(exeItem.lon, exeItem.lat); const wgsCoords = coordtransform.wgs84togcj02(exeItem.lon, exeItem.lat);
@ -775,7 +830,7 @@ onMounted(async () => {
return [mainPosition, targetPosition]; return [mainPosition, targetPosition];
}, false), }, false),
width: 3, width: 3,
material: createFlowMaterial(entityItem.status), material: createFlowMaterial(0),
// 使线 // 使线
enableDepthTest: true 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) : ''; 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" <ShorePowerUsage v-show="activeHeadGroup === 1" :realtime-device-data-time="realtimeDeviceDataTime"
:realtime-device-data="realtimeDeviceData" :active-head-group="activeHeadGroup" v-if="dataLoad" :realtime-device-data="realtimeDeviceData" :active-head-group="activeHeadGroup"
:handleGoBack="handleGoBack" /> :initial-time-range="selectedTimeRange" v-if="dataLoad" :handleGoBack="handleGoBack" />
<!-- 港口岸电使用情况单数据 --> <!-- 港口岸电使用情况单数据 -->
<ShorePowerUsageSingleData v-if="activeHeadGroup === 5 && dataLoad" <ShorePowerUsageSingleData v-if="activeHeadGroup === 5 && dataLoad"
:realtime-device-data-time="realtimeDeviceDataTime" :realtime-device-data="realtimeDeviceData" :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"> --> <!-- <template v-if="activeHeadGroup === 2"> -->
@ -283,6 +284,7 @@ const headGroupList = ref<{ value: number, label: string }[]>([
]) ])
const activeHeadGroup = ref<number>(0) const activeHeadGroup = ref<number>(0)
const selectedTimeRange = ref<string>('day')
// //
interface PublicMapComponentsType { interface PublicMapComponentsType {
@ -325,6 +327,10 @@ const yearDataRes = ref<any>({})
const monthDataRes = ref<any>({}) const monthDataRes = ref<any>({})
const dayDataRes = ref<any>({}) const dayDataRes = ref<any>({})
// const allPortAreaCount = ref<number>(0)
const comparativeData = ref<ComparativeData>({}) const comparativeData = ref<ComparativeData>({})
@ -386,6 +392,7 @@ const chartData = ref({
const companyShorePowerBuildData = ref<CompanyShorePowerBuildDataItem | null>(null) const companyShorePowerBuildData = ref<CompanyShorePowerBuildDataItem | null>(null)
const shipTotalData = ref<any>({ const shipTotalData = ref<any>({
'allPortAreaCount': 0,
'berthingShips': 0, 'berthingShips': 0,
'shorePowerShips': 0, 'shorePowerShips': 0,
'noShorePowerShips': 0, 'noShorePowerShips': 0,
@ -713,8 +720,6 @@ onMounted(async () => {
yearDataRes.value = await MapApi.getYearDataByIdList(params); yearDataRes.value = await MapApi.getYearDataByIdList(params);
monthDataRes.value = await MapApi.getMonthDataByIdList(params); monthDataRes.value = await MapApi.getMonthDataByIdList(params);
dayDataRes.value = await MapApi.getDayDataByIdList(params); dayDataRes.value = await MapApi.getDayDataByIdList(params);
const applyShipCount = await MapApi.getApplyShipCount()
console.log('applyShipCount', applyShipCount)
handleGetlRangeData(firstData) handleGetlRangeData(firstData)
handleBuildCompanyShorePower(firstData) handleBuildCompanyShorePower(firstData)
@ -811,8 +816,16 @@ const shorePowerStatusData = computed(() => {
}) })
}) })
const handleGoToModule = (moduleType: number) => { const handleGoToModule = (moduleType: number, timeRange?: string) => {
activeHeadGroup.value = moduleType activeHeadGroup.value = moduleType
// 1使
if ([1, 5].includes(moduleType) && timeRange) {
// ShorePowerUsage
console.log('timeRange', timeRange)
// ShorePowerUsage
selectedTimeRange.value = timeRange
}
} }
const handleGoBack = () => { const handleGoBack = () => {
activeHeadGroup.value = 0 activeHeadGroup.value = 0
@ -1058,18 +1071,22 @@ const handleBuildCompanyShorePower = (realtimeDeviceData: RealtimeDeviceData[])
// console.log(buildData); // console.log(buildData);
} }
const handleBuildCompanyShorePowerYear = (shipData: any) => { const handleBuildCompanyShorePowerYear = async (shipData: any) => {
const applyShipCount = await MapApi.getApplyShipCount() as number
shipTotalData.value = { shipTotalData.value = {
'allPortAreaCount': applyShipCount,
'berthingShips': shipData.length, 'berthingShips': shipData.length,
'shorePowerShips': shipData.filter(ship => ['在用'].includes(ship.shipStatus || '')).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 至昨日此时
* - 日同比今日 00:00 至当前 vs 去年今日 00:00 至去年此时
* - 月环比本月 01 日至当前 vs 上月 01 日至上月同日对齐天数 * - 月环比本月 01 日至当前 vs 上月 01 日至上月同日对齐天数
* - 月同比本月 01 日至当前 vs 去年同月 01 日至去年同日对齐天数
* - 年环比本年 01-01 至当前 vs 去年 01-01 至去年同日对齐天数 * - 年环比本年 01-01 至当前 vs 去年 01-01 至去年同日对齐天数
*/ */
const handleBuildTimeComparison = async (): Promise<ComparativeData> => { const handleBuildTimeComparison = async (): Promise<ComparativeData> => {
@ -1107,18 +1124,31 @@ const handleBuildTimeComparison = async (): Promise<ComparativeData> => {
const yesterdayStart = yesterday.startOf('day').valueOf(); const yesterdayStart = yesterday.startOf('day').valueOf();
const yesterdayEnd = yesterday.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 thisMonthStart = now.startOf('month').valueOf();
const thisMonthEnd = now.valueOf(); const thisMonthEnd = now.valueOf();
const lastMonth = now.subtract(1, 'month'); const lastMonth = now.subtract(1, 'month');
const lastMonthStart = lastMonth.startOf('month').valueOf(); const lastMonthStart = lastMonth.startOf('month').valueOf();
// 3130 // ""3130
const daysInLastMonth = lastMonth.daysInMonth(); const daysInLastMonth = lastMonth.daysInMonth();
const todayDate = now.date(); const todayDate = now.date();
const alignedDay = Math.min(todayDate, daysInLastMonth); const alignedDay = Math.min(todayDate, daysInLastMonth);
const lastMonthEnd = lastMonth.date(alignedDay).endOf('day').valueOf(); 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 thisYearStart = now.startOf('year').valueOf();
const thisYearEnd = now.valueOf(); const thisYearEnd = now.valueOf();
@ -1135,19 +1165,23 @@ const handleBuildTimeComparison = async (): Promise<ComparativeData> => {
lastYearEnd = lastYear.month(now.month()).endOf('month').valueOf(); lastYearEnd = lastYear.month(now.month()).endOf('month').valueOf();
} }
// ===== 6 ===== // ===== 8 =====
const [ const [
todayRes, todayRes,
yesterdayRes, yesterdayRes,
lastYearTodayRes,
thisMonthRes, thisMonthRes,
lastMonthRes, lastMonthRes,
lastYearMonthRes,
thisYearRes, thisYearRes,
lastYearRes lastYearRes
] = await Promise.all([ ] = await Promise.all([
MapApi.getByStartAndEndTimeAndTimeType({ start: todayStart, end: todayEnd, timeType: 2 }), MapApi.getByStartAndEndTimeAndTimeType({ start: todayStart, end: todayEnd, timeType: 2 }),
MapApi.getByStartAndEndTimeAndTimeType({ start: yesterdayStart, end: yesterdayEnd, 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: thisMonthStart, end: thisMonthEnd, timeType: 3 }),
MapApi.getByStartAndEndTimeAndTimeType({ start: lastMonthStart, end: lastMonthEnd, 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: thisYearStart, end: thisYearEnd, timeType: 4 }),
MapApi.getByStartAndEndTimeAndTimeType({ start: lastYearStart, end: lastYearEnd, timeType: 4 }) MapApi.getByStartAndEndTimeAndTimeType({ start: lastYearStart, end: lastYearEnd, timeType: 4 })
]); ]);
@ -1155,8 +1189,10 @@ const handleBuildTimeComparison = async (): Promise<ComparativeData> => {
// ===== ===== // ===== =====
const todayUsage = calculatePeriodUsage(todayRes); const todayUsage = calculatePeriodUsage(todayRes);
const yesterdayUsage = calculatePeriodUsage(yesterdayRes); const yesterdayUsage = calculatePeriodUsage(yesterdayRes);
const lastYearTodayUsage = calculatePeriodUsage(lastYearTodayRes);
const thisMonthUsage = calculatePeriodUsage(thisMonthRes); const thisMonthUsage = calculatePeriodUsage(thisMonthRes);
const lastMonthUsage = calculatePeriodUsage(lastMonthRes); const lastMonthUsage = calculatePeriodUsage(lastMonthRes);
const lastYearMonthUsage = calculatePeriodUsage(lastYearMonthRes);
const thisYearUsage = calculatePeriodUsage(thisYearRes); const thisYearUsage = calculatePeriodUsage(thisYearRes);
const lastYearUsage = calculatePeriodUsage(lastYearRes); const lastYearUsage = calculatePeriodUsage(lastYearRes);
@ -1167,11 +1203,21 @@ const handleBuildTimeComparison = async (): Promise<ComparativeData> => {
current: { period: now.format('YYYY-MM-DD'), value: todayUsage }, current: { period: now.format('YYYY-MM-DD'), value: todayUsage },
previous: { period: yesterday.format('YYYY-MM-DD'), value: yesterdayUsage } 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: { month: {
growthRate: calculateGrowthRate(thisMonthUsage, lastMonthUsage), growthRate: calculateGrowthRate(thisMonthUsage, lastMonthUsage),
current: { period: now.format('YYYY-MM'), value: thisMonthUsage }, current: { period: now.format('YYYY-MM'), value: thisMonthUsage },
previous: { period: lastMonth.format('YYYY-MM'), value: lastMonthUsage } 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: { year: {
growthRate: calculateGrowthRate(thisYearUsage, lastYearUsage), growthRate: calculateGrowthRate(thisYearUsage, lastYearUsage),
current: { period: now.format('YYYY'), value: thisYearUsage }, current: { period: now.format('YYYY'), value: thisYearUsage },

22
src/types/shorepower.d.ts

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

Loading…
Cancel
Save