|
|
@ -1,8 +1,9 @@ |
|
|
<template> |
|
|
<template> |
|
|
<div class="show-data"> |
|
|
<div class="show-data"> |
|
|
<div class="left"> |
|
|
<div v-if="showShipList || shorePowerCardShow" 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 v-if="showShipList" class="card digital-twin-card--deep-blue " style=" display: inline-block; width: 420px;"> |
|
|
<div v-if="showShipList && !shorePowerCardShow" 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 +51,7 @@ |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div v-if="shipSelectedItem && showShipList" class="card digital-twin-card--deep-blue" |
|
|
<div v-if="shipSelectedItem && showShipList && !shorePowerCardShow" 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> |
|
|
@ -64,7 +65,7 @@ |
|
|
<span class="label">名称:</span> |
|
|
<span class="label">名称:</span> |
|
|
<span class="value">{{ shipSelectedItem.shipBasicInfo.name + '-' + |
|
|
<span class="value">{{ shipSelectedItem.shipBasicInfo.name + '-' + |
|
|
shipSelectedItem.shipBasicInfo.nameEn |
|
|
shipSelectedItem.shipBasicInfo.nameEn |
|
|
}}</span> |
|
|
}}</span> |
|
|
</div> |
|
|
</div> |
|
|
<!-- <div class="detail-item"> |
|
|
<!-- <div class="detail-item"> |
|
|
<span class="label">英文船名:</span> |
|
|
<span class="label">英文船名:</span> |
|
|
@ -118,7 +119,7 @@ |
|
|
<span class="label">靠泊类型:</span> |
|
|
<span class="label">靠泊类型:</span> |
|
|
<span class="value">{{ getOperationTypeLabel(shipSelectedItem.shorePowerAndShip.type, |
|
|
<span class="value">{{ getOperationTypeLabel(shipSelectedItem.shorePowerAndShip.type, |
|
|
BERTH_TYPE) |
|
|
BERTH_TYPE) |
|
|
}}</span> |
|
|
}}</span> |
|
|
</div> |
|
|
</div> |
|
|
<div class="detail-item"> |
|
|
<div class="detail-item"> |
|
|
<span class="label">靠泊时间:</span> |
|
|
<span class="label">靠泊时间:</span> |
|
|
@ -129,7 +130,7 @@ |
|
|
<span class="label">当前状态:</span> |
|
|
<span class="label">当前状态:</span> |
|
|
<span class="value">{{ shipSelectedItem.shipStatus }}<span v-if="shipSelectedItem.StatusReason">({{ |
|
|
<span class="value">{{ shipSelectedItem.shipStatus }}<span v-if="shipSelectedItem.StatusReason">({{ |
|
|
shipSelectedItem.StatusReason |
|
|
shipSelectedItem.StatusReason |
|
|
}})</span></span> |
|
|
}})</span></span> |
|
|
</div> |
|
|
</div> |
|
|
<div v-if="shipSelectedItem.applyInfo.reason === 0" class="detail-item"> |
|
|
<div v-if="shipSelectedItem.applyInfo.reason === 0" class="detail-item"> |
|
|
<span class="label">岸电使用时长</span> |
|
|
<span class="label">岸电使用时长</span> |
|
|
@ -147,17 +148,17 @@ |
|
|
<div class="detail-item"> |
|
|
<div class="detail-item"> |
|
|
<span class="label">岸电联系人:</span> |
|
|
<span class="label">岸电联系人:</span> |
|
|
<span class="value">{{ shipSelectedItem?.shipBasicInfo?.shorePowerContact |
|
|
<span class="value">{{ shipSelectedItem?.shipBasicInfo?.shorePowerContact |
|
|
}}</span> |
|
|
}}</span> |
|
|
</div> |
|
|
</div> |
|
|
<div class="detail-item"> |
|
|
<div class="detail-item"> |
|
|
<span class="label">联系方式:</span> |
|
|
<span class="label">联系方式:</span> |
|
|
<span class="value">{{ shipSelectedItem?.shipBasicInfo?.shorePowerContactPhone |
|
|
<span class="value">{{ shipSelectedItem?.shipBasicInfo?.shorePowerContactPhone |
|
|
}}</span> |
|
|
}}</span> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div v-if="shorePowerCardShow" style="width: 840px"> |
|
|
<div v-if="shorePowerCardShow && !showShipList" style="width: 840px"> |
|
|
<ShorePowerUsageRate :realtimeDeviceDataTime="realtimeDeviceDataTime" :handleClose="handleCloseRate" /> |
|
|
<ShorePowerUsageRate :realtimeDeviceDataTime="realtimeDeviceDataTime" :handleClose="handleCloseRate" /> |
|
|
|
|
|
|
|
|
</div> |
|
|
</div> |
|
|
@ -253,14 +254,23 @@ |
|
|
</div> |
|
|
</div> |
|
|
<div class="enterprise-usage"> |
|
|
<div class="enterprise-usage"> |
|
|
<div class="usage-bars"> |
|
|
<div class="usage-bars"> |
|
|
<div class="bar-item" v-for="item in companyShorePowerBuildData[enterpriseActivePeriod]" |
|
|
<!-- <div class="bar-item" v-for="item in companyShorePowerBuildData[enterpriseActivePeriod]" |
|
|
:key="item.harborDistrictId"> |
|
|
:key="item.harborDistrictId"> |
|
|
<div class="bar-header"> |
|
|
<div class="bar-header"> |
|
|
<span class="bar-name">{{ item.name }}</span> |
|
|
<span class="bar-name">{{ item.name }}</span> |
|
|
<span class="bar-value">{{ item.totalPower.toFixed(2) }} kWH</span> |
|
|
<span class="bar-value">{{ item.totalPower.toFixed(2) }} kWH</span> |
|
|
</div> |
|
|
</div> |
|
|
<el-progress |
|
|
<el-progress |
|
|
:percentage="calculatePercentage(item.totalPower, companyShorePowerBuildData[enterpriseActivePeriod])" |
|
|
:percentage="calculatePercentageForCompany(item, companyShorePowerData)" |
|
|
|
|
|
:show-text="false" :stroke-width="10" /> |
|
|
|
|
|
</div> --> |
|
|
|
|
|
<div class="bar-item" v-for="item in companyShorePowerData" :key="item.id"> |
|
|
|
|
|
<div class="bar-header"> |
|
|
|
|
|
<span class="bar-name">{{ item.name }}</span> |
|
|
|
|
|
<span class="bar-value">{{ handGetDeviceData(item.shorePowerIds, |
|
|
|
|
|
getCompanyTimeRange(item.id)) }} kWH</span> |
|
|
|
|
|
</div> |
|
|
|
|
|
<el-progress :percentage="calculatePercentageForCompany(item, companyShorePowerData)" |
|
|
:show-text="false" :stroke-width="10" /> |
|
|
:show-text="false" :stroke-width="10" /> |
|
|
</div> |
|
|
</div> |
|
|
<!-- <div class="bar-item"> |
|
|
<!-- <div class="bar-item"> |
|
|
@ -380,10 +390,11 @@ |
|
|
</template> |
|
|
</template> |
|
|
|
|
|
|
|
|
<script setup lang="ts"> |
|
|
<script setup lang="ts"> |
|
|
import { CompanyShorePowerBuildDataItem, RealtimeDeviceData, ShipRespVo, TimeRange } from '@/types/shorepower'; |
|
|
import { CompanyShorePowerBuildDataItem, CompanyShorePowerData, RealtimeDeviceData, ShipRespVo, TimeRange } from '@/types/shorepower'; |
|
|
import { ref, computed } from 'vue' |
|
|
import { ref, computed } from 'vue' |
|
|
import { MapApi } from "@/api/shorepower/map"; |
|
|
import { MapApi } from "@/api/shorepower/map"; |
|
|
import ShipHistoryDialog from './ShipHistoryDialog.vue' |
|
|
import ShipHistoryDialog from './ShipHistoryDialog.vue' |
|
|
|
|
|
import ShorePowerUsageRate from './ShorePowerUsageRate.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 { |
|
|
@ -409,6 +420,10 @@ interface Props { |
|
|
'noShorePowerShips': number, |
|
|
'noShorePowerShips': number, |
|
|
} |
|
|
} |
|
|
shipDataList: ShipRespVo[] |
|
|
shipDataList: ShipRespVo[] |
|
|
|
|
|
companyShorePowerData: CompanyShorePowerData[] |
|
|
|
|
|
yearData: any[]; |
|
|
|
|
|
monthData: any[]; |
|
|
|
|
|
dayData: any[]; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const props = defineProps<Props>() |
|
|
const props = defineProps<Props>() |
|
|
@ -434,9 +449,9 @@ const updateTime = computed(() => { |
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
|
// 时间段筛选相关 - 为每个模块创建独立的状态 |
|
|
// 时间段筛选相关 - 为每个模块创建独立的状态 |
|
|
const portActivePeriod = ref('day') // 港口岸电使用情况模块的时间粒度 |
|
|
const portActivePeriod = ref<'realtime' | 'day' | 'month' | 'year'>('day') // 港口岸电使用情况模块的时间粒度 |
|
|
const enterpriseActivePeriod = ref('day') // 港口企业岸电使用模块的时间粒度 |
|
|
const enterpriseActivePeriod = ref<'realtime' | 'day' | 'month' | 'year'>('day') // 港口企业岸电使用模块的时间粒度 |
|
|
const shipActivePeriod = ref('day') // 船舶岸电使用情况模块的时间粒度 |
|
|
const shipActivePeriod = ref<'realtime' | 'day' | 'month' | 'year'>('day') // 船舶岸电使用情况模块的时间粒度 |
|
|
const filterType = ref('all') // 船舶岸电使用情况模块的筛选类型 |
|
|
const filterType = ref('all') // 船舶岸电使用情况模块的筛选类型 |
|
|
|
|
|
|
|
|
const shipHistoryVisible = ref({ |
|
|
const shipHistoryVisible = ref({ |
|
|
@ -449,7 +464,7 @@ const shipHistoryVisible = ref({ |
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
|
const shipSelectedItem = ref<ShipRespVo | null>(null) |
|
|
const shipSelectedItem = ref<ShipRespVo | null>(null) |
|
|
const shorePowerCardShow = ref(true) |
|
|
const shorePowerCardShow = ref(false) |
|
|
|
|
|
|
|
|
const periodOptions = [ |
|
|
const periodOptions = [ |
|
|
{ label: '当日', value: 'day' }, |
|
|
{ label: '当日', value: 'day' }, |
|
|
@ -490,6 +505,7 @@ const handleCloseRate = () => { |
|
|
const handleOpenRate = () => { |
|
|
const handleOpenRate = () => { |
|
|
handleCloseShipList() |
|
|
handleCloseShipList() |
|
|
shorePowerCardShow.value = true |
|
|
shorePowerCardShow.value = true |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -597,10 +613,6 @@ const shipStartTime = computed(() => { |
|
|
return `${year}-${month}-${day} ${hours}:${minutes}` |
|
|
return `${year}-${month}-${day} ${hours}:${minutes}` |
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
|
// 获取当前选中时间段的数据 |
|
|
|
|
|
const currentRankingData = computed(() => { |
|
|
|
|
|
return rankingData[activePeriod.value as keyof typeof rankingData] || [] |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
// 计算岸电使用率 |
|
|
// 计算岸电使用率 |
|
|
const calculateShorePowerUsageRate = (): number => { |
|
|
const calculateShorePowerUsageRate = (): number => { |
|
|
@ -611,33 +623,36 @@ const calculateShorePowerUsageRate = (): number => { |
|
|
return Number(usageRate.toFixed(2)) |
|
|
return Number(usageRate.toFixed(2)) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 计算公司用电量的进度条百分比 |
|
|
|
|
|
const calculatePercentageForCompany = (company: CompanyShorePowerData, companyData: CompanyShorePowerData[]): number => { |
|
|
|
|
|
if (!companyData || companyData.length === 0) return 0 |
|
|
|
|
|
|
|
|
|
|
|
// 计算当前公司的用电量 |
|
|
|
|
|
const companyPower = handGetDeviceData(company.shorePowerIds, getCompanyTimeRange(company.id)) |
|
|
|
|
|
|
|
|
// 计算进度条百分比 |
|
|
// 计算所有公司的最大用电量 |
|
|
const calculatePercentage = (value: number, dataArray: typeof props.companyShorePowerBuildData[TimeRange]): number => { |
|
|
const maxPower = Math.max(...companyData.map(item => { |
|
|
if (!dataArray || dataArray.length === 0) return 0 |
|
|
return parseFloat(handGetDeviceData(item.shorePowerIds, getCompanyTimeRange(item.id))) |
|
|
|
|
|
})) |
|
|
// 找到数组中的最大值 |
|
|
|
|
|
const maxValue = Math.max(...dataArray.map(item => item.totalPower)) |
|
|
|
|
|
|
|
|
|
|
|
// 如果最大值为0,返回0以避免除以0 |
|
|
// 如果最大值为0,返回0以避免除以0 |
|
|
if (maxValue === 0) return 0 |
|
|
if (maxPower === 0) return 0 |
|
|
|
|
|
|
|
|
// 计算百分比,最小值设为5%以确保即使是最小的值也能在视觉上显示 |
|
|
// 计算百分比,最小值设为5%以确保即使是最小的值也能在视觉上显示 |
|
|
const percentage = (value / maxValue) * 100 |
|
|
const percentage = (parseFloat(companyPower) / maxPower) * 100 |
|
|
return Math.max(percentage, 5) |
|
|
return Math.max(percentage, 5) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 切换时间段 - 为每个模块创建独立的切换方法 |
|
|
// 切换时间段 - 为每个模块创建独立的切换方法 |
|
|
const switchPortPeriod = (period: string) => { |
|
|
const switchPortPeriod = (period: 'realtime' | 'day' | 'month' | 'year') => { |
|
|
portActivePeriod.value = period |
|
|
portActivePeriod.value = period |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const switchEnterprisePeriod = (period: string) => { |
|
|
const switchEnterprisePeriod = (period: 'realtime' | 'day' | 'month' | 'year') => { |
|
|
enterpriseActivePeriod.value = period |
|
|
enterpriseActivePeriod.value = period |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const switchShipPeriod = (period: string) => { |
|
|
const switchShipPeriod = (period: 'realtime' | 'day' | 'month' | 'year') => { |
|
|
shipActivePeriod.value = period |
|
|
shipActivePeriod.value = period |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -758,6 +773,63 @@ const handleCloseShipList = () => { |
|
|
const handleCloseShipDetail = () => { |
|
|
const handleCloseShipDetail = () => { |
|
|
shipSelectedItem.value = null |
|
|
shipSelectedItem.value = null |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const handGetDeviceData = (deviceId: number | number[], range: 'realtime' | 'day' | 'month' | 'year') => { |
|
|
|
|
|
let totalValue = 0; |
|
|
|
|
|
let rangData: RealtimeDeviceData[] = [] |
|
|
|
|
|
if (range === 'realtime') { |
|
|
|
|
|
rangData = props.realtimeDeviceData |
|
|
|
|
|
} else if (range === 'day') { |
|
|
|
|
|
rangData = props.dayData |
|
|
|
|
|
} else if (range === 'month') { |
|
|
|
|
|
rangData = props.monthData |
|
|
|
|
|
} else if (range === 'year') { |
|
|
|
|
|
rangData = props.yearData |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (Array.isArray(deviceId)) { |
|
|
|
|
|
// If deviceId is an array, sum all the measure values and subtract start values |
|
|
|
|
|
|
|
|
|
|
|
deviceId.forEach(id => { |
|
|
|
|
|
const deviceData = deviceDataMap.value.get(id); |
|
|
|
|
|
const startData = rangData.find(item => item.deviceId === id) |
|
|
|
|
|
|
|
|
|
|
|
if (deviceData?.measureValue) { |
|
|
|
|
|
totalValue += deviceData.measureValue; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// For non-realtime ranges, subtract the start value |
|
|
|
|
|
if (range !== 'realtime' && startData?.measureValue) { |
|
|
|
|
|
totalValue -= startData.measureValue; |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
} else { |
|
|
|
|
|
// If deviceId is a single number, get its measure value |
|
|
|
|
|
const deviceData = deviceDataMap.value.get(deviceId); |
|
|
|
|
|
const startData = rangData.find(item => item.deviceId === deviceId) |
|
|
|
|
|
|
|
|
|
|
|
if (deviceData?.measureValue) { |
|
|
|
|
|
totalValue = deviceData.measureValue; |
|
|
|
|
|
} |
|
|
|
|
|
if (range !== 'realtime' && startData?.measureValue) { |
|
|
|
|
|
totalValue -= startData.measureValue |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return totalValue.toFixed(2); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const getCompanyTimeRange = (companyId: number): 'realtime' | 'day' | 'month' | 'year' => { |
|
|
|
|
|
return enterpriseActivePeriod.value |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const deviceDataMap = computed(() => { |
|
|
|
|
|
const map = new Map<number, RealtimeDeviceData>(); |
|
|
|
|
|
props.realtimeDeviceData.forEach(item => { |
|
|
|
|
|
map.set(item.deviceId, item); |
|
|
|
|
|
}); |
|
|
|
|
|
return map; |
|
|
|
|
|
}); |
|
|
</script> |
|
|
</script> |
|
|
|
|
|
|
|
|
<style scoped lang="scss"> |
|
|
<style scoped lang="scss"> |
|
|
|