运营菜单下挂地图监控/任务/报警入口,车辆卡片与任务列表同步展示运行态。 Co-authored-by: Cursor <cursoragent@cursor.com>
150 lines
4.5 KiB
TypeScript
150 lines
4.5 KiB
TypeScript
import { computed, toValue, type MaybeRefOrGetter } from 'vue'
|
|
import type { VehicleCardModel } from '@/types/car'
|
|
|
|
export interface VehicleCardStateOptions {
|
|
vehicle: MaybeRefOrGetter<VehicleCardModel>
|
|
}
|
|
|
|
const stateLabels: Record<string, string> = {
|
|
idle: '空闲',
|
|
running: '运行',
|
|
charging: '充电',
|
|
paused: '暂停',
|
|
fault: '故障',
|
|
offline: '离线'
|
|
}
|
|
|
|
function formatMissionIdSuffix(missionId?: string | number | null): string {
|
|
if (missionId == null) return ''
|
|
const id = String(missionId).trim()
|
|
if (!id || id === '0') return ''
|
|
return `-${id}`
|
|
}
|
|
|
|
function appendMissionId(base: string, missionId?: string | number | null): string {
|
|
const suffix = formatMissionIdSuffix(missionId)
|
|
return suffix ? `${base}${suffix}` : base
|
|
}
|
|
|
|
export function useVehicleCardState(opts: VehicleCardStateOptions) {
|
|
const vehicle = computed(() => toValue(opts.vehicle))
|
|
|
|
const stateLabel = computed(() => stateLabels[vehicle.value.state] ?? vehicle.value.state)
|
|
|
|
const batteryPct = computed(() => {
|
|
const raw = vehicle.value.batterySoc ?? 0
|
|
const pct = raw > 1 ? raw : raw * 100
|
|
return Math.max(0, Math.min(100, Math.round(pct)))
|
|
})
|
|
|
|
const batteryTone = computed(() => {
|
|
const p = batteryPct.value
|
|
if (p < 20) return 'tone-danger'
|
|
if (p < 50) return 'tone-warning'
|
|
return 'tone-success'
|
|
})
|
|
|
|
const switchOn = computed(() => vehicle.value.maintenanceMode === 'online')
|
|
|
|
const statusPill = computed(() => {
|
|
const v = vehicle.value
|
|
if (v.reachable === false) return { label: '不可达', tone: 'tone-danger' }
|
|
if (v.isAlarmActive) return { label: '报警中', tone: 'tone-danger' }
|
|
if (v.maintenanceMode === 'offline') return { label: '下线维护', tone: 'tone-warning' }
|
|
if (v.maintenanceMode === 'repair') return { label: '现场检修', tone: 'tone-warning' }
|
|
if (v.maintenanceMode === 'blown') return { label: '返厂检修', tone: 'tone-danger' }
|
|
if (v.state === 'fault') return { label: '故障', tone: 'tone-danger' }
|
|
if (v.state === 'running') return { label: '运行中', tone: 'tone-success' }
|
|
if (v.state === 'charging') return { label: '充电中', tone: 'tone-info' }
|
|
if (v.state === 'offline') return { label: '离线', tone: 'tone-idle' }
|
|
return { label: stateLabel.value, tone: 'tone-idle' }
|
|
})
|
|
|
|
const missionIdSuffix = computed(() => formatMissionIdSuffix(vehicle.value.missionId))
|
|
|
|
const statusDisplayLabel = computed(() =>
|
|
appendMissionId(statusPill.value.label, vehicle.value.missionId)
|
|
)
|
|
|
|
const runtimeStatusLabel = computed(() =>
|
|
appendMissionId(vehicle.value.lstatus ?? stateLabel.value, vehicle.value.missionId)
|
|
)
|
|
|
|
const accentTone = computed(() => statusPill.value.tone)
|
|
|
|
const latencyLabel = computed(() => {
|
|
const ms = vehicle.value.latencyMs
|
|
if (vehicle.value.reachable === false && ms == null) return '不可达'
|
|
if (ms == null) return '—'
|
|
return `${ms} ms`
|
|
})
|
|
|
|
const latencyClass = computed(() => {
|
|
const ms = vehicle.value.latencyMs
|
|
if (vehicle.value.reachable === false) return 'val-danger'
|
|
// WatchDog TCP RTT:局域网正常多在几十毫秒;>150 警示,>400 危险
|
|
if (ms != null && ms > 400) return 'val-danger'
|
|
if (ms != null && ms > 150) return 'val-warn'
|
|
return ''
|
|
})
|
|
|
|
const faultLabel = computed(() => {
|
|
const v = vehicle.value.faultRatePercent
|
|
if (v == null) return '—'
|
|
return `${v.toFixed(2)}%`
|
|
})
|
|
|
|
const faultClass = computed(() => {
|
|
const v = vehicle.value.faultRatePercent ?? 0
|
|
if (v >= 5) return 'val-danger'
|
|
if (v >= 1) return 'val-warn'
|
|
return ''
|
|
})
|
|
|
|
const cpuLabel = computed(() => {
|
|
const v = vehicle.value.cpuPercent
|
|
return v != null ? `${Math.round(v)}%` : '—'
|
|
})
|
|
|
|
const memLabel = computed(() => {
|
|
const v = vehicle.value.memPercent
|
|
return v != null ? `${Math.round(v)}%` : '—'
|
|
})
|
|
|
|
const cpuChipClass = computed(() => {
|
|
const v = vehicle.value.cpuPercent
|
|
if (v == null) return ''
|
|
if (v >= 90) return 'val-danger'
|
|
if (v >= 75) return 'val-warn'
|
|
return 'val-ok'
|
|
})
|
|
|
|
const memChipClass = computed(() => {
|
|
const v = vehicle.value.memPercent
|
|
if (v == null) return ''
|
|
if (v >= 90) return 'val-danger'
|
|
if (v >= 75) return 'val-warn'
|
|
return 'val-ok'
|
|
})
|
|
|
|
return {
|
|
stateLabel,
|
|
batteryPct,
|
|
batteryTone,
|
|
switchOn,
|
|
statusPill,
|
|
missionIdSuffix,
|
|
statusDisplayLabel,
|
|
runtimeStatusLabel,
|
|
accentTone,
|
|
latencyLabel,
|
|
latencyClass,
|
|
faultLabel,
|
|
faultClass,
|
|
cpuLabel,
|
|
memLabel,
|
|
cpuChipClass,
|
|
memChipClass
|
|
}
|
|
}
|