Browse Source

update

feature
jiangAB 1 week ago
parent
commit
8d5f906b8a
  1. 48
      public/map/components/CompanyShorePower.vue
  2. 27
      public/map/components/cesiumMap.vue
  3. 21
      public/map/components/dictionaryTable.ts
  4. 175
      public/map/index.vue

48
public/map/components/CompanyShorePower.vue

@ -28,8 +28,9 @@
<span class="ship-name-text">{{ shorepower.name }}</span>
</div>
<div class="ship-table-column ship-status">
<div class="status-tag" :class="getStatusClass(shorepower.storePowerStatus)">
{{ shorepower.storePowerStatus }}
<div class="status-tag"
:class="getStatusClass(getOperationTypeLabel(shorePowerStatus(shorepower), SHORE_POWER_FIRST_STATUS))">
{{ getOperationTypeLabel(shorePowerStatus(shorepower), SHORE_POWER_FIRST_STATUS) }}
</div>
<!-- <div class="status-tag" :class="getStatusClass('空闲')">
空闲
@ -104,7 +105,11 @@
</div>
<div class="detail-item">
<span class="label">当前状态:</span>
<span class="value">{{ selectedShorePowerItem.storePowerStatus }}</span>
<span class="value">{{ getOperationTypeLabel(shorePowerStatus(selectedShorePowerItem),
SHORE_POWER_FIRST_STATUS,
) }}({{ getOperationTypeLabel(shorePowerStatus(selectedShorePowerItem),
SHORE_POWER_SECOND_STATUS_MAP,
) }})</span>
</div>
</div>
</div>
@ -184,6 +189,7 @@ import { MapApi } from "@/api/shorepower/map";
import ShipHistoryDialog from './ShipHistoryDialog.vue';
import { CompanyShorePowerData, RealtimeDeviceData, ShipRespVo, ShorePowerBerth } from '@/types/shorepower';
import { getValueById } from './utils';
import { getOperationTypeLabel, SHORE_POWER_FIRST_STATUS, SHORE_POWER_SECOND_STATUS_MAP } from './dictionaryTable';
//
@ -233,28 +239,34 @@ const handlStoreSearch = () => {
//
}
const shorePowerStatus = (item: ShorePowerBerth) => {
if (!item.totalPowerDeviceId) {
return ''
}
const status = getDeviceStatus(item.totalPowerDeviceId)
if (status === null) {
return ''
}
return status
}
/* 用户实时设备状态 */
const getDeviceStatus = (deviceId: number) => {
const data = props.realtimeDeviceData.find(item => item.deviceId === deviceId)
if (!data) {
return null
}
return data.deviceStatus
}
const getStatusClass = (status: string | undefined) => {
switch (status) {
case '正常':
return 'status-normal'
return 'status-default'
case '在用':
return 'status-normal'
case '在线':
return 'status-normal'
case '空闲':
return 'status-idle'
case '故障':
return 'status-fault'
case '超容':
return 'status-maintenance'
case '异常':
return 'status-abnormal'
case '维修中':
return 'status-maintenance'
case '岸电使用中':
return 'status-shorepower'
// case '':
// return 'status-fault'
default:
return 'status-normal'
}

27
public/map/components/cesiumMap.vue

@ -66,6 +66,10 @@ const props = defineProps({
onInstanceClick: {
type: Function,
default: (data) => { }
},
getDeviceStatus: {
type: Function,
default: (deviceIds) => { }
}
})
@ -362,7 +366,7 @@ onMounted(async () => {
}
})
.map(item => JSON.parse(item.data));
console.log(result)
// console.log(result)
if (!result || result.length === 0) return
const dataObj = result[0]
let longitude, latitude;
@ -446,8 +450,10 @@ onMounted(async () => {
})
console.log(distributionDataList)
shorePowerList.forEach((item) => {
// console.log(item.totalPowerDeviceId)
props.getDeviceStatus(item.totalPowerDeviceId)
const itemShipInfo = dataInfo.filter(mapitem => (item.id === mapitem.parentId) && mapitem.type === 5)
console.log(item)
// console.log(item)
const result = itemShipInfo
.filter(item => {
try {
@ -536,7 +542,7 @@ onMounted(async () => {
}
});
let statusImage = null;
/* let statusImage = null;
if ([3, 4, 6].includes(item.status)) {
// 50%
statusImage = null;
@ -568,11 +574,11 @@ onMounted(async () => {
}, false)
}
});
}
} */
labelInstance.push(xelectricalBoxLabel)
})
shipData.forEach(item => {
console.log(item.shorePower.id)
// console.log(item.shorePower.id)
const itemShipInfo = dataInfo.filter(shipItem => (item.shorePower.id === shipItem.parentId) && shipItem.type === 5)
const result = itemShipInfo
.filter(item => {
@ -632,7 +638,7 @@ onMounted(async () => {
data: item
})
let labelColor = null
let statusImage = null;
/* let statusImage = null;
if (['在用', '正常'].includes(item.shipStatus)) {
// statusImage = null;
} else if (item.shipStatus === '超容') {
@ -667,7 +673,7 @@ onMounted(async () => {
}, false)
}
});
}
} */
const MODEL_HEIGHT_METERS = 80;
const cartographic = Cesium.Cartographic.fromCartesian(xposition);
@ -780,12 +786,11 @@ onMounted(async () => {
}
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);
// console.warn('Missing entity or position property:', entityItem);
return;
}
if (entityItem.status !== '在用') return;
@ -819,7 +824,7 @@ onMounted(async () => {
}
function createDistributionBoxAndShorePowerLine() {
distributionBoxAndShorePowerInstance.forEach((entityItem, idx) => {
console.log('distributionBoxAndShorePowerInstance', entityItem)
// console.log('distributionBoxAndShorePowerInstance', entityItem)
const distributionBoxEntity = entityItem.distributionBoxModel
const shorePowerEntity = entityItem.storePowerInstance
// const entity = entityItem.xelectricalBoxModel
@ -964,7 +969,7 @@ const switchView = (viewName) => {
*/
const switchModelView = (item, extraDistance = 1000, estimatedRadius = 1000.0) => {
const clickItem = toRaw(item)
console.log(item)
// console.log(item)
let entityInstance = null
if (clickItem.type === 'shorepower_box') {
console.log(clickItem.item)

21
public/map/components/dictionaryTable.ts

@ -135,3 +135,24 @@ export const SHIP_STATUS = [
{ label: '异常', value: 4 },
]
/* 岸电一级状态 */
export const SHORE_POWER_FIRST_STATUS = [
{ label: '故障', value: 1 }, // 检修
{ label: '故障', value: 2 }, // 跳闸
{ label: '在用', value: 3 }, // 合闸 → 正在供电使用
{ label: '正常', value: 4 }, // 分闸 → 空闲但正常
{ label: '故障', value: 5 }, // 通信异常
{ label: '故障', value: 8 }, // 无法解析设备状态
{ label: '正常', value: 9 }, // 未开始采集数据
]
/* 岸电二级状态 */
export const SHORE_POWER_SECOND_STATUS_MAP = [
{ label: '检修', value: 1 },
{ label: '跳闸', value: 2 },
{ label: '合闸', value: 3 },
{ label: '分闸', value: 4 },
{ label: '通信异常', value: 5 },
{ label: '无法解析设备状态', value: 8 },
{ label: '未开始采集数据', value: 9 },
]

175
public/map/index.vue

@ -2,7 +2,7 @@
<div>
<PublicMapComponents ref="mapComponentRef" class="map-base" :shore-power-list="ShorePowerList"
:ship-data-list="shipDataList" v-if="dataLoad" :on-instance-click="handleElectricalBoxClick"
:distribution-data-list="distributionDataList" />
:distribution-data-list="distributionDataList" :get-device-status="getDeviceStatus" />
<div v-else class="loading">
<div class="loading-container">
<el-space direction="vertical" size="large">
@ -240,7 +240,11 @@
</div>
<div class="detail-item">
<span class="label">当前状态:</span>
<span class="value">{{ selectedElectricalBox.data?.storePowerStatus }}</span>
<span class="value">{{ getOperationTypeLabel(shorePowerStatus,
SHORE_POWER_FIRST_STATUS,
) }}({{ getOperationTypeLabel(shorePowerStatus,
SHORE_POWER_SECOND_STATUS_MAP,
) }})</span>
</div>
</div>
</div>
@ -305,7 +309,7 @@ import { onMounted, onUnmounted, ref, computed, watch } from 'vue'
import { Loading } from '@element-plus/icons-vue'
import { MapApi } from "@/api/shorepower/map";
import { CompanyShorePowerBuildDataItem, CompanyShorePowerData, ComparativeData, RealtimeDeviceData, ShipBasicInfoRespVO, ShipRespVo, ShorePowerBerth, TimeRange } from '@/types/shorepower'
import { BERTH_TYPE, DOCK_DISTRICT, getOperationTypeLabel, HARBOR_DISTRICT, SHORE_POWER_STATUS, UNUSED_SHORE_POWER_REASON } from './components/dictionaryTable'
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
@ -451,6 +455,26 @@ const updateTime = () => {
currentTime.value = dayjs().format('YYYY-MM-DD HH:mm:ss')
}
const shorePowerStatus = computed(() => {
if (!selectedElectricalBox.value.data?.totalPowerDeviceId) {
return ''
}
const status = getDeviceStatus(selectedElectricalBox.value.data?.totalPowerDeviceId)
if (status === null) {
return ''
}
return status
})
/* 用户实时设备状态 */
const getDeviceStatus = (deviceId: number) => {
const data = realtimeDeviceData.value.find(item => item.deviceId === deviceId)
if (!data) {
return null
}
return data.deviceStatus
}
const selectedShip = ref(null)
const selectedElectricalBox = ref({
type: '',
@ -776,7 +800,7 @@ onMounted(async () => {
handleBuildCompanyShorePower(firstData)
const buildComparativeData = await handleBuildTimeComparison()
console.log('buildComparativeData', buildComparativeData)
// console.log('buildComparativeData', buildComparativeData)
comparativeData.value = buildComparativeData
await handleGetStorePower()
@ -784,6 +808,8 @@ onMounted(async () => {
handleBuildCompanyShorePowerYear(tempBuildShipData)
getDistributionBoxDataList()
await handleGetBuildData()
buildShorePowerUsageRatio()
dataLoad.value = true
getRealtimeIntervalId = setInterval(async () => {
const data = await handleGetRealtimeAllData()
@ -1338,6 +1364,147 @@ const handleBuildTimeComparison = async (): Promise<ComparativeData> => {
}
};
};
/**
* 构建岸电使用率环比和同比
*/
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();
}
// ===== 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 {
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 }
}
};
};
}
</script>
<style lang="scss">

Loading…
Cancel
Save