Files
momentry_core/portal/src/components/IdentitySwimlane.vue

81 lines
2.6 KiB
Vue

<template>
<div class="space-y-2">
<div class="flex items-center justify-between">
<h3 class="text-sm font-semibold text-gray-300">身分泳道圖 V2</h3>
<span class="text-xs text-gray-500">{{ identities.length }} identities</span>
</div>
<div class="relative overflow-x-auto" ref="scrollRef">
<svg :width="svgW" :height="rowH * identities.length + 30" class="block">
<!-- time axis -->
<line x1="80" :y1="rowH * identities.length + 5" :x2="svgW" :y2="rowH * identities.length + 5" stroke="#4b5563" stroke-width="1" />
<g v-for="t in ticks" :key="t">
<line :x1="xPos(t)" :y1="rowH * identities.length + 1" :x2="xPos(t)" :y2="rowH * identities.length + 5" stroke="#6b7280" stroke-width="1" />
<text :x="xPos(t)" :y="rowH * identities.length + 16" fill="#9ca3af" font-size="9" text-anchor="middle">{{ t }}s</text>
</g>
<!-- swimlanes -->
<g v-for="(ident, i) in identities" :key="ident.name">
<text x="4" :y="rowH * i + rowH / 2 + 5" fill="#d1d5db" font-size="11" class="select-none">{{ ident.name }}</text>
<rect x="78" :y="rowH * i + 4" width="2" :height="rowH - 8" fill="#374151" rx="2" />
<rect
v-for="seg in ident.segments" :key="seg.start"
:x="xPos(seg.start)" :y="rowH * i + 6"
:width="Math.max(2, xPos(seg.end) - xPos(seg.start))"
:height="rowH - 12"
:fill="ident.color"
:opacity="0.7"
rx="3"
class="cursor-pointer hover:opacity-100"
@click="$emit('selectTrace', seg.trace_id)"
/>
</g>
</svg>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
const props = defineProps<{
identities: SwimlaneIdentity[]
totalDuration: number
}>()
defineEmits<{ selectTrace: [traceId: number] }>()
export interface SwimlaneSegment {
trace_id: number
start: number
end: number
face_count: number
}
export interface SwimlaneIdentity {
name: string
color: string
segments: SwimlaneSegment[]
}
const rowH = 28
const labelW = 80
const padR = 20
const svgW = computed(() => {
const dur = props.totalDuration || 6000
return Math.max(500, labelW + dur / 8)
})
function xPos(sec: number): number {
const dur = props.totalDuration || 6000
return labelW + (sec / dur) * (svgW.value - labelW - padR)
}
const ticks = computed(() => {
const dur = props.totalDuration || 6000
const step = Math.max(30, Math.round(dur / 6 / 30) * 30)
const tks: number[] = []
for (let t = 0; t <= dur; t += step) tks.push(t)
return tks
})
</script>