|
|
|
@ -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(); |
|
|
|
// 上月可能没有"今天"这个日期(如今天31号,上月只有30天),取最小值避免跨月 |
|
|
|
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"> |
|
|
|
|