mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-11 03:39:49 +08:00
113 lines
3.4 KiB
Vue
113 lines
3.4 KiB
Vue
<template>
|
||||
|
|
<div class="w-full space-y-1">
|
|||
|
|
<div class="flex h-6 w-full items-center gap-px">
|
|||
|
|
<TooltipProvider
|
|||
|
|
v-for="(segment, index) in segments"
|
|||
|
|
:key="index"
|
|||
|
|
:delay-duration="100"
|
|||
|
|
>
|
|||
|
|
<Tooltip>
|
|||
|
|
<TooltipTrigger as-child>
|
|||
|
|
<div
|
|||
|
|
class="h-full flex-1 cursor-pointer rounded-sm transition-all duration-150 hover:scale-y-110 hover:brightness-110"
|
|||
|
|
:class="getTimelineColor(segment.status)"
|
|||
|
|
/>
|
|||
|
|
</TooltipTrigger>
|
|||
|
|
<TooltipContent
|
|||
|
|
side="top"
|
|||
|
|
:side-offset="8"
|
|||
|
|
class="max-w-xs"
|
|||
|
|
>
|
|||
|
|
<div class="text-xs whitespace-pre-line">
|
|||
|
|
{{ segment.tooltip }}
|
|||
|
|
</div>
|
|||
|
|
</TooltipContent>
|
|||
|
|
</Tooltip>
|
|||
|
|
</TooltipProvider>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="flex items-center justify-between text-[10px] text-muted-foreground">
|
|||
|
|
<span>{{ startLabel }}</span>
|
|||
|
|
<span>{{ endLabel }}</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import { computed } from 'vue'
|
|||
|
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
|
|||
|
|
import {
|
|||
|
|
formatTimestamp,
|
|||
|
|
getTimelineColor,
|
|||
|
|
getTimelineLabel
|
|||
|
|
} from './health-monitor-utils'
|
|||
|
|
|
|||
|
|
const props = withDefaults(defineProps<{
|
|||
|
|
timeline?: string[] | null
|
|||
|
|
timeRangeStart?: string | null
|
|||
|
|
timeRangeEnd?: string | null
|
|||
|
|
generatedAt?: string | null
|
|||
|
|
lookbackHours?: number
|
|||
|
|
fallbackSegments?: number
|
|||
|
|
entityLabel?: string
|
|||
|
|
entityName?: string | null
|
|||
|
|
}>(), {
|
|||
|
|
lookbackHours: 6,
|
|||
|
|
fallbackSegments: 60,
|
|||
|
|
entityLabel: '',
|
|||
|
|
entityName: null
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const statuses = computed(() => {
|
|||
|
|
if (Array.isArray(props.timeline) && props.timeline.length > 0) {
|
|||
|
|
return props.timeline
|
|||
|
|
}
|
|||
|
|
return Array.from({ length: props.fallbackSegments }, () => 'unknown')
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const startMs = computed(() => {
|
|||
|
|
const explicitStart = props.timeRangeStart
|
|||
|
|
? new Date(props.timeRangeStart).getTime()
|
|||
|
|
: NaN
|
|||
|
|
if (!Number.isNaN(explicitStart)) return explicitStart
|
|||
|
|
return endMs.value - props.lookbackHours * 60 * 60 * 1000
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const endMs = computed(() => {
|
|||
|
|
const explicitEnd = props.timeRangeEnd
|
|||
|
|
? new Date(props.timeRangeEnd).getTime()
|
|||
|
|
: NaN
|
|||
|
|
if (!Number.isNaN(explicitEnd)) return explicitEnd
|
|||
|
|
const generatedAt = props.generatedAt
|
|||
|
|
? new Date(props.generatedAt).getTime()
|
|||
|
|
: NaN
|
|||
|
|
if (!Number.isNaN(generatedAt)) return generatedAt
|
|||
|
|
return Date.now()
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const startLabel = computed(() => formatTimestamp(new Date(startMs.value).toISOString()))
|
|||
|
|
const endLabel = computed(() => formatTimestamp(new Date(endMs.value).toISOString()))
|
|||
|
|
|
|||
|
|
const segments = computed(() => {
|
|||
|
|
const segmentStatuses = statuses.value
|
|||
|
|
const safeRange = Math.max(endMs.value - startMs.value, 1)
|
|||
|
|
const interval = safeRange / segmentStatuses.length
|
|||
|
|
|
|||
|
|
return segmentStatuses.map((status, index) => {
|
|||
|
|
const cellStart = new Date(startMs.value + index * interval).toISOString()
|
|||
|
|
const cellEnd = new Date(startMs.value + (index + 1) * interval).toISOString()
|
|||
|
|
return {
|
|||
|
|
status,
|
|||
|
|
tooltip: buildTooltip(status, cellStart, cellEnd)
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
function buildTooltip(status: string, cellStart: string, cellEnd: string) {
|
|||
|
|
const entity = props.entityLabel && props.entityName
|
|||
|
|
? `\n${props.entityLabel}:${props.entityName}`
|
|||
|
|
: ''
|
|||
|
|
return `${formatTimestamp(cellStart)} - ${formatTimestamp(cellEnd)}${entity}\n状态:${getTimelineLabel(status)}`
|
|||
|
|
}
|
|||
|
|
</script>
|