feat: 新增 JsonViewer 和 AiCallLogDetailDialog 组件

This commit is contained in:
2026-05-24 11:48:07 +08:00
parent 902068387b
commit 3888a40a5c
2 changed files with 287 additions and 0 deletions
@@ -0,0 +1,164 @@
<template>
<el-dialog
v-model="visible"
title="调用详情"
width="800px"
:close-on-click-modal="true"
:close-on-press-escape="true"
destroy-on-close
>
<div v-if="log" class="detail-dialog">
<div class="detail-section">
<div class="section-title">基本信息</div>
<div class="info-grid">
<div class="info-item">
<span class="info-label">调用时间</span>
<span class="info-value">{{ log.createTime }}</span>
</div>
<div class="info-item">
<span class="info-label">场景</span>
<span class="info-value">{{ log.sceneCode || '-' }}</span>
</div>
<div class="info-item">
<span class="info-label">服务商</span>
<span class="info-value">{{ log.providerCode || '-' }}</span>
</div>
<div class="info-item">
<span class="info-label">接口</span>
<span class="info-value">{{ log.endpointCode || '-' }}</span>
</div>
<div class="info-item">
<span class="info-label">状态</span>
<span class="info-value">
<el-tag :type="statusType(log.status)" size="small" effect="plain">
{{ statusLabel(log.status) }}
</el-tag>
</span>
</div>
<div class="info-item">
<span class="info-label">首字耗时</span>
<span class="info-value">{{ formatMs(log.firstTokenMs) }}</span>
</div>
<div class="info-item">
<span class="info-label">总耗时</span>
<span class="info-value">{{ formatMs(log.durationMs) }}</span>
</div>
<div class="info-item">
<span class="info-label">片段数</span>
<span class="info-value">{{ log.streamChunks ?? '-' }}</span>
</div>
<div class="info-item">
<span class="info-label">用户 ID</span>
<span class="info-value">{{ log.userId || '-' }}</span>
</div>
<div class="info-item">
<span class="info-label">请求 ID</span>
<span class="info-value">{{ log.requestId || '-' }}</span>
</div>
</div>
</div>
<div class="detail-section">
<JsonViewer title="入参" :data="log.inputText" />
</div>
<div class="detail-section">
<JsonViewer title="出参" :data="log.outputText" />
</div>
<div v-if="log.status === 'failed'" class="detail-section">
<div class="section-title">错误信息</div>
<div class="error-box">
{{ log.errorCode }}: {{ log.errorMessage }}
</div>
</div>
</div>
<template #footer>
<el-button @click="visible = false">关闭</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import JsonViewer from '@/components/JsonViewer.vue'
import type { AiCallLog } from '@/types/aiconfig'
interface Props {
modelValue: boolean
log: AiCallLog | null
}
const props = defineProps<Props>()
const emit = defineEmits<{ (e: 'update:modelValue', value: boolean): void }>()
const visible = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
function statusType(status?: string) {
if (status === 'success') return 'success'
if (status === 'failed') return 'danger'
return 'warning'
}
function statusLabel(status?: string) {
const map: Record<string, string> = { running: '运行中', success: '成功', failed: '失败' }
return status ? map[status] || status : '-'
}
function formatMs(ms?: number) {
if (ms == null) return '-'
return ms >= 1000 ? `${(ms / 1000).toFixed(2)}s` : `${ms}ms`
}
</script>
<style scoped>
.detail-dialog {
display: flex;
flex-direction: column;
gap: 20px;
}
.detail-section {
display: flex;
flex-direction: column;
gap: 10px;
}
.section-title {
color: rgba(226, 232, 240, 0.85);
font-size: 14px;
font-weight: 600;
}
.info-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px 16px;
}
.info-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.info-label {
color: var(--ls-text-muted);
font-size: 12px;
}
.info-value {
color: rgba(226, 232, 240, 0.92);
font-size: 13px;
}
.error-box {
padding: 12px;
color: #f56c6c;
font-size: 13px;
background: rgba(245, 108, 108, 0.08);
border: 1px solid rgba(245, 108, 108, 0.2);
border-radius: 6px;
}
@media (max-width: 768px) {
.info-grid {
grid-template-columns: repeat(2, 1fr);
}
}
</style>