feat(frontend): 对接工作台页面真实接口

This commit is contained in:
2026-06-01 04:53:05 +08:00
parent 2dd242c54b
commit 8f7ffd6cc9
6 changed files with 670 additions and 51 deletions

View File

@@ -1,5 +1,75 @@
<script setup lang="ts">
import { recentRuns, traceSteps } from '@/data/studioMock';
import { computed, onMounted, ref } from 'vue';
import { ElMessage } from 'element-plus';
import type { ObservabilityRunSummary, ObservabilityTrace } from '@/api/observability';
import { exportObservabilityTrace, getObservabilityTrace, listObservabilityRuns } from '@/api/observability';
const loading = ref(false);
const runs = ref<ObservabilityRunSummary[]>([]);
const trace = ref<ObservabilityTrace | null>(null);
const exportSummary = ref('');
const selectedRequestId = computed(() => trace.value?.requestId || runs.value[0]?.requestId || '');
function formatLatency(durationMs?: number) {
if (durationMs == null) {
return '-';
}
if (durationMs >= 1000) {
return `${(durationMs / 1000).toFixed(2)}s`;
}
return `${durationMs}ms`;
}
function formatCost(cost?: number) {
if (cost == null) {
return '-';
}
return `¥${cost}`;
}
function formatStatus(status?: string) {
if (status === 'SUCCESS') {
return '成功';
}
if (status === 'FAILED') {
return '失败';
}
if (status === 'RUNNING') {
return '运行中';
}
return status || '-';
}
async function loadRuns() {
loading.value = true;
try {
const response = await listObservabilityRuns();
runs.value = response.data ?? [];
if (runs.value.length > 0) {
await loadTrace(runs.value[0].requestId);
}
} finally {
loading.value = false;
}
}
async function loadTrace(requestId: string) {
const response = await getObservabilityTrace(requestId);
trace.value = response.data;
}
async function exportTrace() {
if (!selectedRequestId.value) {
return;
}
const response = await exportObservabilityTrace(selectedRequestId.value);
exportSummary.value = response.data.exportSummary ?? '';
ElMessage.success('脱敏日志导出成功');
}
onMounted(loadRuns);
</script>
<template>
@@ -9,10 +79,10 @@ import { recentRuns, traceSteps } from '@/data/studioMock';
<p class="studio-kicker">ObservabilityView</p>
<h1>运行观测</h1>
</div>
<el-button>导出日志</el-button>
<el-button data-test="observability-export" @click="exportTrace">导出日志</el-button>
</header>
<div class="observability-layout">
<div class="observability-layout" v-loading="loading">
<section class="studio-panel">
<div class="panel-heading">
<h2>运行记录</h2>
@@ -20,18 +90,23 @@ import { recentRuns, traceSteps } from '@/data/studioMock';
</div>
<div class="run-table">
<div class="run-row run-head">
<span>名称</span><span>类型</span><span>状态</span><span>延迟</span><span>成本</span>
<span>请求ID</span><span>状态</span><span>延迟</span><span>成本</span>
</div>
<div v-for="run in recentRuns" :key="run.id" class="run-row">
<strong>{{ run.name }}</strong>
<span>{{ run.type }}</span>
<div
v-for="run in runs"
:key="run.requestId"
class="run-row"
:data-test="`observability-run-${run.requestId}`"
@click="loadTrace(run.requestId)"
>
<strong>{{ run.requestId }}</strong>
<span class="status-cell">
<span class="status-pill" :class="run.status === '成功' ? 'is-success' : 'is-warning'">
{{ run.status }}
<span class="status-pill" :class="formatStatus(run.status) === '成功' ? 'is-success' : 'is-warning'">
{{ formatStatus(run.status) }}
</span>
</span>
<span>{{ run.latency }}</span>
<span>{{ run.cost }}</span>
<span>{{ formatLatency(run.durationMs) }}</span>
<span>{{ formatCost(run.estimatedCost) }}</span>
</div>
</div>
</section>
@@ -39,12 +114,13 @@ import { recentRuns, traceSteps } from '@/data/studioMock';
<aside class="studio-panel">
<div class="panel-heading compact">
<h2>步骤日志</h2>
<span>run-1842</span>
<span data-test="observability-trace-id">{{ selectedRequestId || '暂无请求' }}</span>
</div>
<p v-if="exportSummary" class="export-summary" data-test="observability-export-summary">{{ exportSummary }}</p>
<ol class="log-list">
<li v-for="step in traceSteps" :key="step.node">
<time>{{ step.duration }}</time>
<span>{{ step.node }} · {{ step.status }} · {{ step.output }}</span>
<li v-for="step in trace?.stepLogs ?? []" :key="`${step.nodeName}-${step.durationMs}`" :data-test="`observability-step-${step.nodeName}`">
<time>{{ formatLatency(step.durationMs) }}</time>
<span>{{ step.nodeName }} · {{ formatStatus(step.status) }} · {{ step.outputSummary || step.errorMessage || '-' }}</span>
</li>
</ol>
</aside>