Browse Source

update

feature
jiangAB 7 days ago
parent
commit
7ceb1c731c
  1. 92
      public/map/components/ShorePowerUsageRate.vue
  2. 81
      public/map/components/ShorePowerUsageSingleData.vue
  3. 6
      public/map/components/ShowData.vue
  4. 16
      public/map/components/cesiumMap.vue
  5. 24
      public/map/components/charts/ComparisonBarChart.vue
  6. 27
      public/map/components/dictionaryTable.ts
  7. 276
      public/map/index.vue
  8. 5
      src/types/shorepower.d.ts

92
public/map/components/ShorePowerUsageRate.vue

@ -40,17 +40,17 @@
<div v-if="timeRange === 'year'" class="card-content">
<ComparisonBarChart :chartData="getYearlyData" title="年度用电量对比" yAxisName="用电量(kWh)" currentColor="#36A2EB"
previousColor="#FF6384" />
previousColor="#FF6384" chartType="percentage" />
</div>
<div v-if="timeRange === 'month'" class="card-content">
<ComparisonBarChart :chartData="getMonthlyComparisonData"
:title="comparisonType === 'yearOnYear' ? '月度用电量同比对比' : '月度用电量环比对比'" yAxisName="用电量(kWh)"
currentColor="#36A2EB" previousColor="#FF6384" />
currentColor="#36A2EB" previousColor="#FF6384" chartType="percentage" />
</div>
<div class="card-content" v-if="timeRange === 'day'">
<ComparisonBarChart :chartData="getDailyComparisonData"
:title="comparisonType === 'yearOnYear' ? '日用电量同比对比' : '日用电量环比对比'" yAxisName="用电量(kWh)" currentColor="#36A2EB"
previousColor="#FF6384" />
previousColor="#FF6384" chartType="percentage" />
</div>
</div>
</div>
@ -60,14 +60,14 @@
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';
import { ComparativeData, MetricData, RealtimeDeviceData } from '@/types/shorepower';
interface Props {
// realtimeDeviceData: RealtimeDeviceData[];
// activeHeadGroup?: number;
handleClose: () => void;
realtimeDeviceDataTime: string;
// comparativeData: ComparativeData;
comparativeData: MetricData;
initialTimeRange?: 'realtime' | 'day' | 'month' | 'year';
}
@ -125,75 +125,43 @@ export interface deviceData {
const getYearlyData = computed(() => {
return [{
name: '本期上期对比',
growthRate: .11,
currentValue: 22,
previousValue: 33,
currentPeriod: '2024',
previousPeriod: '2025',
growthRate: props.comparativeData.year.growthRate,
currentValue: convertPowerUsage(props.comparativeData.year.current.value, selectedCard.value),
previousValue: convertPowerUsage(props.comparativeData.year.previous.value, selectedCard.value),
currentPeriod: props.comparativeData.year.current.period,
previousPeriod: props.comparativeData.year.previous.period,
}]
})
// -
//
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], //
}
};
const comparisonData = comparisonType.value === 'yearOnYear'
? props.comparativeData.dayYearOnYear
: props.comparativeData.day;
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,
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 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), //
}
};
const comparisonData = comparisonType.value === 'yearOnYear'
? props.comparativeData.monthYearOnYear
: props.comparativeData.month;
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,
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,
}]
})

81
public/map/components/ShorePowerUsageSingleData.vue

@ -219,55 +219,6 @@
</div>
</div>
</div>
<!-- <div class="one-row">
<div v-for="card in cards.filter(c => c.id !== selectedCard)" :key="card.id"
class="card digital-twin-card--deep-blue" @click="handleSelectCard(card.id)">
<div style="display: flex; align-items: center; justify-content: space-between;">
<div class="card-title">
<div class="vertical-line"></div>
<img src="@/assets/svgs/data.svg" class="title-icon" />
<span class="title-text">{{ card.title }}</span>
</div>
<div v-if="card.type === 'overview'" class="time-range-selector">
</div>
<div v-if="card.type === 'chart'" class="show-value">
<span class="show-value-label">{{timeRangeOptions.find(option => option.value === timeRange)?.label ||
''}}:</span>
<div class="show-value-value">{{ chartData[timeRange][card.value] }}</div>
</div>
</div>
<div class="card-content">
<div v-if="card.type === 'overview'" class="overview-grid">
<div class="overview-item">
<div class="overview-value">{{ historyData.totalPower }}</div>
<div class="overview-label">区间用电(千瓦时)</div>
</div>
<div class="overview-item">
<div class="overview-value">{{ historyData.fuel }}</div>
<div class="overview-label">减少燃油()</div>
</div>
<div class="overview-item">
<div class="overview-value">{{ historyData.co2 }}</div>
<div class="overview-label">减少二氧化碳排放(千克)</div>
</div>
<div class="overview-item">
<div class="overview-value">{{ historyData.pm25 }}</div>
<div class="overview-label">减少PM2.5排放(千克)</div>
</div>
<div class="overview-item">
<div class="overview-value">{{ historyData.nox }}</div>
<div class="overview-label">减少氮氧化物(千克)</div>
</div>
<div class="overview-item">
<div class="overview-value">{{ historyData.so2 }}</div>
<div class="overview-label">减少二氧化硫(千克)</div>
</div>
</div>
<WaveLineChart v-else :chart-data="getHistoryChartData(card.id)" :title="card.title"
:color="card.color" />
</div>
</div>
</div> -->
</div>
</template>
@ -280,7 +231,7 @@ import WaveLineChart from './charts/WaveLineChart.vue'
import { MapApi } from "@/api/shorepower/map";
import dayjs from 'dayjs';
import ComparisonBarChart from './charts/ComparisonBarChart.vue'
import { ComparativeData, RealtimeDeviceData } from '@/types/shorepower';
import { MetricData, RealtimeDeviceData } from '@/types/shorepower';
import { formatTimestamp, parseRangeToTimestamp } from './utils';
@ -289,7 +240,7 @@ interface Props {
activeHeadGroup?: number;
handleGoBack: () => void;
realtimeDeviceDataTime: string;
comparativeData: ComparativeData;
comparativeData: MetricData; //
initialTimeRange?: 'realtime' | 'day' | 'month' | 'year';
yearData: RealtimeDeviceData[];
monthData: RealtimeDeviceData[];
@ -374,35 +325,7 @@ export interface deviceData {
const pageType = ref<'realtime' | 'history'>('realtime')
const dateRange = ref<string[]>([])
const totalPower = ref<string>('0')
const fuelReduction = ref<string>('0')
const co2Reduction = ref<string>('0')
const pm25Reduction = ref<string>('0')
const noxReduction = ref<string>('0')
const so2Reduction = ref<string>('0')
// const yearlyData = ref<ComparativeData['year']>({})
const getDailyData = computed(() => {
return [{
name: '本期上期对比',
growthRate: props.comparativeData.day.growthRate,
currentValue: convertPowerUsage(props.comparativeData.day.current.value, selectedCard.value),
previousValue: convertPowerUsage(props.comparativeData.day.previous.value, selectedCard.value),
currentPeriod: props.comparativeData.day.current.period,
previousPeriod: props.comparativeData.day.previous.period,
}]
})
const getMonthlyData = computed(() => {
return [{
name: '本期上期对比',
growthRate: props.comparativeData.month.growthRate,
currentValue: convertPowerUsage(props.comparativeData.month.current.value, selectedCard.value),
previousValue: convertPowerUsage(props.comparativeData.month.previous.value, selectedCard.value),
currentPeriod: props.comparativeData.month.current.period,
previousPeriod: props.comparativeData.month.previous.period,
}]
})
const getYearlyData = computed(() => {
return [{
name: '本期上期对比',

6
public/map/components/ShowData.vue

@ -159,7 +159,8 @@
</div>
</div>
<div v-if="shorePowerCardShow && !showShipList" style="width: 840px">
<ShorePowerUsageRate :realtimeDeviceDataTime="realtimeDeviceDataTime" :handleClose="handleCloseRate" />
<ShorePowerUsageRate :realtimeDeviceDataTime="realtimeDeviceDataTime" :comparative-data="comparativeData"
:handleClose="handleCloseRate" />
</div>
<!-- <div v-if="shorePowerCardShow" class="card digital-twin-card--deep-blue"
@ -380,7 +381,7 @@
</template>
<script setup lang="ts">
import { CompanyShorePowerData, RealtimeDeviceData, ShipRespVo, TimeRange } from '@/types/shorepower';
import { CompanyShorePowerData, MetricData, RealtimeDeviceData, ShipRespVo, TimeRange } from '@/types/shorepower';
import { ref, computed } from 'vue'
import { MapApi } from "@/api/shorepower/map";
import ShipHistoryDialog from './ShipHistoryDialog.vue'
@ -412,6 +413,7 @@ interface Props {
yearData: any[];
monthData: any[];
dayData: any[];
comparativeData: MetricData; // 使
}
const props = defineProps<Props>()

16
public/map/components/cesiumMap.vue

@ -541,7 +541,9 @@ onMounted(async () => {
scaleByDistance: new Cesium.NearFarScalar(1000, 1.0, 50000, 0.1)
}
});
const status = props.getDeviceStatus(item.totalPowerDeviceId)
console.log(status)
// if ([1,2,5,8].includes(status)) {}
/* let statusImage = null;
if ([3, 4, 6].includes(item.status)) {
// 50%
@ -552,9 +554,9 @@ onMounted(async () => {
} else {
// 25%线
statusImage = '/img/离线.png';
}
} */
//
if (statusImage) {
if ([1, 2, 5, 8].includes(status)) {
const overlayBillboard = viewer.entities.add({
position: xposition,
properties: {
@ -562,7 +564,7 @@ onMounted(async () => {
data: item
},
billboard: {
image: statusImage,
image: '/img/故障.png',
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.CENTER,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
@ -574,7 +576,7 @@ onMounted(async () => {
}, false)
}
});
} */
}
labelInstance.push(xelectricalBoxLabel)
})
shipData.forEach(item => {
@ -638,7 +640,7 @@ onMounted(async () => {
data: item
})
let labelColor = null
/* let statusImage = null;
let statusImage = null;
if (['在用', '正常'].includes(item.shipStatus)) {
// statusImage = null;
} else if (item.shipStatus === '超容') {
@ -673,7 +675,7 @@ onMounted(async () => {
}, false)
}
});
} */
}
const MODEL_HEIGHT_METERS = 80;
const cartographic = Cesium.Cartographic.fromCartesian(xposition);

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

@ -23,6 +23,7 @@ interface Props {
yAxisName?: string
currentLabel?: string
previousLabel?: string
chartType?: 'default' | 'percentage'
}
const props = withDefaults(defineProps<Props>(), {
@ -33,7 +34,8 @@ const props = withDefaults(defineProps<Props>(), {
showYAxis: true,
yAxisName: '数值',
currentLabel: '本期',
previousLabel: '上期'
previousLabel: '上期',
chartType: 'default'
})
//
@ -42,10 +44,18 @@ let chartInstance: echarts.ECharts | null = null
//
const createBarOption = (currentLabel: string, previousLabel: string, currentValue: number, previousValue: number) => {
//
const isPercentage = props.chartType === 'percentage';
return {
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' }
axisPointer: { type: 'shadow' },
formatter: (params: any) => {
const value = params[0].value;
const formattedValue = isPercentage ? `${(value * 100).toFixed(1)}%` : value.toFixed(2);
return `${params[0].name}: ${formattedValue}`;
}
},
grid: {
left: '15%',
@ -66,7 +76,7 @@ const createBarOption = (currentLabel: string, previousLabel: string, currentVal
yAxis: {
type: 'value',
axisLabel: {
formatter: '{value}',
formatter: isPercentage ? (value: number) => `${(value * 100).toFixed(0)}%` : '{value}',
color: 'rgba(255, 255, 255, 0.7)'
},
splitLine: {
@ -78,10 +88,11 @@ const createBarOption = (currentLabel: string, previousLabel: string, currentVal
lineStyle: {
color: 'rgba(255, 255, 255, 0.3)'
}
}
},
max: isPercentage ? 1 : undefined
},
series: [{
name: '用量',
name: isPercentage ? '百分比' : '用量',
type: 'bar',
barWidth: '60%',
data: [
@ -94,7 +105,8 @@ const createBarOption = (currentLabel: string, previousLabel: string, currentVal
fontWeight: 'bold',
color: '#fff',
formatter: (params: any) => {
return params.value > 0 ? params.value.toFixed(0) : '';
if (params.value <= 0) return '';
return isPercentage ? `${(params.value * 100).toFixed(1)}%` : params.value.toFixed(2);
}
}
}],

27
public/map/components/dictionaryTable.ts

@ -128,13 +128,6 @@ export const APPLY_STATUS = [
{ label: '已取消', value: 11 },
]
export const SHIP_STATUS = [
{ label: '在用', value: 1 },
{ label: '故障', value: 2 },
{ label: '故障', value: 3 },
{ label: '异常', value: 4 },
]
/* 岸电一级状态 */
export const SHORE_POWER_FIRST_STATUS = [
{ label: '故障', value: 1 }, // 检修
@ -155,4 +148,22 @@ export const SHORE_POWER_SECOND_STATUS_MAP = [
{ label: '通信异常', value: 5 },
{ label: '无法解析设备状态', value: 8 },
{ label: '未开始采集数据', value: 9 },
]
]
/* 船舶一级状态 */
export const SHIP_FIRST_STATUS = [
{ label: '在用', value: 1 },
{ label: '故障', value: 2 },
{ label: '正常', value: 3 },
{ label: '异常', value: 4 },
]
/* 船舶二级状态 */
export const SHIP_SECOND_STATUS_MAP = [
{ label: '在用', value: 1 },
{ label: '超容', value: 2 },
{ label: '故障', value: 3 },
{ label: '获取岸电容量失败', value: 4 },
{ label: '未使用岸电', value: 5 },
{ label: '跳闸', value: 6 },
]

276
public/map/index.vue

@ -36,7 +36,7 @@
:realtime-device-data-time="realtimeDeviceDataTime" :chart-data="chartData" :ship-total-data="shipTotalData"
:ship-data-list="shipDataList" @item-click="handleSwitch" :realtime-device-data="realtimeDeviceData"
v-if="dataLoad" :company-shore-power-data="companyComparisonData" :year-data="yearDataRes"
:month-data="monthDataRes" :day-data="dayDataRes" />
:month-data="monthDataRes" :day-data="dayDataRes" :comparative-data="comparativeData.utilizationRate" />
<!-- 港口岸电使用情况多数据 -->
<ShorePowerUsage v-show="activeHeadGroup === 1" :realtime-device-data-time="realtimeDeviceDataTime"
@ -48,7 +48,8 @@
<ShorePowerUsageSingleData v-if="activeHeadGroup === 5 && dataLoad"
:realtime-device-data-time="realtimeDeviceDataTime" :realtime-device-data="realtimeDeviceData"
:active-head-group="activeHeadGroup" :initial-time-range="selectedTimeRange" :handleGoBack="handleGoBack"
:comparative-data="comparativeData" :year-data="yearDataRes" :month-data="monthDataRes" :day-data="dayDataRes" />
:comparative-data="comparativeData.electricityConsumption" :year-data="yearDataRes" :month-data="monthDataRes"
:day-data="dayDataRes" />
<!-- 港口企业岸电使用 -->
<!-- <template v-if="activeHeadGroup === 2"> -->
@ -312,6 +313,7 @@ import { CompanyShorePowerBuildDataItem, CompanyShorePowerData, ComparativeData,
import { BERTH_TYPE, DOCK_DISTRICT, getOperationTypeLabel, HARBOR_DISTRICT, SHORE_POWER_FIRST_STATUS, SHORE_POWER_SECOND_STATUS_MAP, SHORE_POWER_STATUS, UNUSED_SHORE_POWER_REASON } from './components/dictionaryTable'
import { formatDuration, formatTimestamp, getValueById, showStatus } from './components/utils'
defineOptions({ name: 'PublicMap' })
let getRealtimeIntervalId: NodeJS.Timeout | null = null
@ -808,7 +810,6 @@ onMounted(async () => {
handleBuildCompanyShorePowerYear(tempBuildShipData)
getDistributionBoxDataList()
await handleGetBuildData()
buildShorePowerUsageRatio()
dataLoad.value = true
getRealtimeIntervalId = setInterval(async () => {
@ -1231,6 +1232,7 @@ const handleGetBuildData = async () => {
const handleBuildTimeComparison = async (): Promise<ComparativeData> => {
const now = dayjs();
console.log(getShorePowerIds.value, getBerthingShipsIds.value, getDepartingShipsIds.value, getElectricityUsageIds.value)
/**
* 计算每个分组的周期用量last.measureValue - first.measureValue 的总和
*/
@ -1244,6 +1246,36 @@ const handleBuildTimeComparison = async (): Promise<ComparativeData> => {
}, 0);
};
/**
* 计算所有分组中 measureValue 的总和即累计用量
*/
const calculateTotalUsage = (apiResponse: Record<string, { measureValue: number }[]>): number => {
const dataArrays = Object.values(apiResponse).filter(Array.isArray) as { measureValue: number }[][];
return dataArrays.reduce((total, row) => {
const rowSum = row.reduce((sum, item) => sum + item.measureValue, 0);
return total + rowSum;
}, 0);
};
/**
* 根据指定的 deviceId 数组从数据源中筛选出对应的数据
* @param {Object} dataSource - 数据源格式如 {1: [...], 2: [...], ...}
* @param {Array<number|string>} deviceIds - 要筛选的 deviceId 数组 [1, 2]
* @returns {Object} 筛选后的对象只包含 deviceIds 中存在的键
*/
function filterByDeviceIds(dataSource, deviceIds) {
const result = {};
for (const id of deviceIds) {
// 使 String(id) key
const key = String(id);
if (key in dataSource) {
result[key] = dataSource[key];
}
}
return result;
}
/**
* 安全计算环比增长率(current - previous) / previous
* - previous 0 current 也为 0 返回 0
@ -1326,157 +1358,45 @@ 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);
const todayUsage = calculatePeriodUsage(filterByDeviceIds(todayRes, getShorePowerIds.value));
const yesterdayUsage = calculatePeriodUsage(filterByDeviceIds(yesterdayRes, getShorePowerIds.value));
const lastYearTodayUsage = calculatePeriodUsage(filterByDeviceIds(lastYearTodayRes, getShorePowerIds.value));
const thisMonthUsage = calculatePeriodUsage(filterByDeviceIds(thisMonthRes, getShorePowerIds.value));
const lastMonthUsage = calculatePeriodUsage(filterByDeviceIds(lastMonthRes, getShorePowerIds.value));
const lastYearMonthUsage = calculatePeriodUsage(filterByDeviceIds(lastYearMonthRes, getShorePowerIds.value));
const thisYearUsage = calculatePeriodUsage(filterByDeviceIds(thisYearRes, getShorePowerIds.value));
const lastYearUsage = calculatePeriodUsage(filterByDeviceIds(lastYearRes, getShorePowerIds.value));
// ===== 使 =====
const ship_todayUsage = calculateTotalUsage(filterByDeviceIds(todayRes, getElectricityUsageIds.value)) / calculateTotalUsage(filterByDeviceIds(todayRes, getBerthingShipsIds.value));
const ship_yesterdayUsage = calculateTotalUsage(filterByDeviceIds(yesterdayRes, getElectricityUsageIds.value)) / calculateTotalUsage(filterByDeviceIds(yesterdayRes, getBerthingShipsIds.value));
const ship_lastYearTodayUsage = calculateTotalUsage(filterByDeviceIds(lastYearTodayRes, getElectricityUsageIds.value)) / calculateTotalUsage(filterByDeviceIds(lastYearTodayRes, getBerthingShipsIds.value));
const ship_thisMonthUsage = calculateTotalUsage(filterByDeviceIds(thisMonthRes, getElectricityUsageIds.value)) / calculateTotalUsage(filterByDeviceIds(thisMonthRes, getBerthingShipsIds.value));
const ship_lastMonthUsage = calculateTotalUsage(filterByDeviceIds(lastMonthRes, getElectricityUsageIds.value)) / calculateTotalUsage(filterByDeviceIds(lastMonthRes, getBerthingShipsIds.value));
const ship_lastYearMonthUsage = calculateTotalUsage(filterByDeviceIds(lastYearMonthRes, getElectricityUsageIds.value)) / calculateTotalUsage(filterByDeviceIds(lastYearMonthRes, getBerthingShipsIds.value));
const ship_thisYearUsage = calculateTotalUsage(filterByDeviceIds(thisYearRes, getElectricityUsageIds.value)) / calculateTotalUsage(filterByDeviceIds(thisYearRes, getBerthingShipsIds.value));
const ship_lastYearUsage = calculateTotalUsage(filterByDeviceIds(lastYearRes, getElectricityUsageIds.value)) / calculateTotalUsage(filterByDeviceIds(lastYearRes, getBerthingShipsIds.value));
/* const ship_todayUsage = .5
const ship_yesterdayUsage = .2
const ship_lastYearTodayUsage = .3
const ship_thisMonthUsage = .4
const ship_lastMonthUsage = .1
const ship_lastYearMonthUsage = .6
const ship_thisYearUsage = .8
const ship_lastYearUsage = .9 */
/* 计算使用率 */
/* const calculateUsageRate = () => {
const aa = calculateTotalUsage(filterByDeviceIds(todayRes, getBerthingShipsIds.value))
const bb = calculateTotalUsage(filterByDeviceIds(todayRes, getElectricityUsageIds.value))
// ===== =====
return {
day: {
growthRate: calculateGrowthRate(todayUsage, yesterdayUsage),
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 },
previous: { period: lastYear.format('YYYY'), value: lastYearUsage }
}
};
};
/**
* 构建岸电使用率环比和同比
*/
const buildShorePowerUsageRatio = () => {
const handleBuildTimeComparison = async (): Promise<ComparativeData> => {
const now = dayjs();
/**
* 计算每个分组的周期用量last.measureValue - first.measureValue 的总和
*/
const calculatePeriodUsage = (apiResponse: Record<string, { measureValue: number }[]>): number => {
const dataArrays = Object.values(apiResponse).filter(Array.isArray) as { measureValue: number }[][];
return dataArrays.reduce((total, row) => {
if (row.length === 0) return total;
const firstValue = row[0].measureValue;
const lastValue = row[row.length - 1].measureValue;
return total + (lastValue - firstValue);
}, 0);
};
/**
* 安全计算环比增长率(current - previous) / previous
* - previous 0 current 也为 0 返回 0
* - previous 0 current > 0 返回 null表示新增
*/
const calculateGrowthRate = (current: number | null, previous: number | null): number => {
if (previous === 0) {
return current === 0 ? 0 : null; // null 0
}
return (current - previous) / previous;
};
// ===== =====
const todayStart = now.startOf('day').valueOf();
const todayEnd = now.valueOf();
const yesterday = now.subtract(1, 'day');
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
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();
const lastYear = now.subtract(1, 'year');
const lastYearStart = lastYear.startOf('year').valueOf();
// / 2 29
let lastYearEnd: number;
try {
//
lastYearEnd = lastYear.month(now.month()).date(now.date()).endOf('day').valueOf();
} catch {
// 2/29
lastYearEnd = lastYear.month(now.month()).endOf('month').valueOf();
}
calculateUsageRate() */
// ===== 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 })
]);
// ===== =====
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);
// ===== =====
return {
// ===== =====
return {
electricityConsumption: {
day: {
growthRate: calculateGrowthRate(todayUsage, yesterdayUsage),
current: { period: now.format('YYYY-MM-DD'), value: todayUsage },
@ -1502,9 +1422,57 @@ const buildShorePowerUsageRatio = () => {
current: { period: now.format('YYYY'), value: thisYearUsage },
previous: { period: lastYear.format('YYYY'), value: lastYearUsage }
}
};
};
}
},
utilizationRate: {
day: {
growthRate: calculateGrowthRate(ship_todayUsage, ship_yesterdayUsage),
current: { period: now.format('YYYY-MM-DD'), value: ship_todayUsage },
previous: { period: yesterday.format('YYYY-MM-DD'), value: ship_yesterdayUsage }
},
dayYearOnYear: {
growthRate: calculateGrowthRate(ship_todayUsage, ship_lastYearTodayUsage),
current: { period: now.format('YYYY-MM-DD'), value: ship_todayUsage },
previous: { period: lastYearToday.format('YYYY-MM-DD'), value: ship_lastYearTodayUsage }
},
month: {
growthRate: calculateGrowthRate(ship_thisMonthUsage, ship_lastMonthUsage),
current: { period: now.format('YYYY-MM'), value: ship_thisMonthUsage },
previous: { period: lastMonth.format('YYYY-MM'), value: ship_lastMonthUsage }
},
monthYearOnYear: {
growthRate: calculateGrowthRate(ship_thisMonthUsage, ship_lastYearMonthUsage),
current: { period: now.format('YYYY-MM'), value: ship_thisMonthUsage },
previous: { period: lastYearMonth.format('YYYY-MM'), value: ship_lastYearMonthUsage }
},
year: {
growthRate: calculateGrowthRate(ship_thisYearUsage, ship_lastYearUsage),
current: { period: now.format('YYYY'), value: ship_thisYearUsage },
previous: { period: lastYear.format('YYYY'), value: ship_lastYearUsage }
}
}
}
};
const getShorePowerIds = computed(() => {
return realtimeDeviceData.value.filter(item => item.deviceCode.includes('Kwh')).map(item => item.deviceId)
})
/* 靠泊数量 */
const getBerthingShipsIds = computed(() => {
return realtimeDeviceData.value.filter(item => item.deviceCode.includes('arrive')).map(item => item.deviceId)
})
/* 离泊数量 */
const getDepartingShipsIds = computed(() => {
return realtimeDeviceData.value.filter(item => item.deviceCode.includes('depart')).map(item => item.deviceId)
})
/* 用电数量 */
const getElectricityUsageIds = computed(() => {
return realtimeDeviceData.value.filter(item => item.deviceCode.includes('usage')).map(item => item.deviceId)
})
</script>
<style lang="scss">

5
src/types/shorepower.d.ts

@ -495,6 +495,11 @@ type CompanyShorePowerBuildDataItem = {
}
interface ComparativeData {
electricityConsumption: MetricData
utilizationRate: MetricData
}
interface MetricData {
day: {
growthRate: number
current: {

Loading…
Cancel
Save