refactor: 拆分管理后台页面并按需加载

This commit is contained in:
2026-08-03 16:22:03 +08:00
parent b2b56f43bb
commit 861a0ae7d5
11 changed files with 2829 additions and 1445 deletions

View File

@@ -0,0 +1,125 @@
<script setup lang="ts">
import { ElMessage } from "element-plus";
import { ref, watch } from "vue";
import { api } from "../services/api";
import type { AiLogRecord } from "../types/api";
const props = defineProps<{ modelValue: boolean; logId: number | null }>();
const emit = defineEmits<{ "update:modelValue": [value: boolean] }>();
const detail = ref<AiLogRecord | null>(null);
const loading = ref(false);
watch(
() => [props.modelValue, props.logId] as const,
async ([open, logId]) => {
if (!open || !logId) return;
detail.value = null;
loading.value = true;
try {
detail.value = await api.aiLogDetail(logId);
} catch (error) {
ElMessage.error(
error instanceof Error ? error.message : "AI 请求详情加载失败",
);
} finally {
loading.value = false;
}
},
);
function formatMoney(value?: number | null, currency = "CNY") {
if (typeof value !== "number") return "-";
return `${currency || "CNY"} ${value.toFixed(6)}`;
}
</script>
<template>
<el-drawer
:model-value="modelValue"
size="820px"
title="AI 请求详情"
@update:model-value="emit('update:modelValue', $event)"
>
<div v-loading="loading">
<template v-if="detail">
<section class="chat-summary">
<div>
<span>请求ID</span><strong>{{ detail.id }}</strong>
</div>
<div>
<span>会话ID</span><strong>{{ detail.sessionId || "-" }}</strong>
</div>
<div>
<span>消息ID</span><strong>{{ detail.messageId || "-" }}</strong>
</div>
<div>
<span>用户ID</span><strong>{{ detail.userId || "-" }}</strong>
</div>
<div>
<span>模型</span><strong>{{ detail.modelName || "-" }}</strong>
</div>
<div>
<span>状态</span><strong>{{ detail.status }}</strong>
</div>
</section>
<section class="audit-detail-grid">
<span>知识库{{ detail.knowledgeIds || "-" }}</span
><span>命中数{{ detail.retrieveCount }}</span
><span>片段数{{ detail.retrievedChunks?.length || 0 }}</span
><span>输入 Token{{ detail.inputToken || "-" }}</span
><span>输出 Token{{ detail.outputToken || "-" }}</span
><span> Token{{ detail.totalToken || "-" }}</span
><span
>估算成本{{
formatMoney(detail.estimatedCost, detail.currency || "CNY")
}}</span
><span>问题类型{{ detail.questionType || "-" }}</span
><span>知识命中{{ detail.knowledgeHit ? "是" : "否" }}</span
><span>路由原因{{ detail.routeReason || "-" }}</span
><span>耗时{{ detail.costMs || "-" }}ms</span
><span>时间{{ detail.createdAt }}</span>
</section>
<p v-if="detail.errorMessage" class="error-text">
{{ detail.errorMessage }}
</p>
<h3 class="detail-title">召回知识片段</h3>
<section v-if="detail.retrievedChunks?.length" class="retrieval-chunks">
<article
v-for="chunk in detail.retrievedChunks"
:key="`${detail.id}-${chunk.index}`"
class="retrieval-chunk"
>
<div class="chunk-head">
<strong
>片段 {{ chunk.index }}/{{
detail.retrievedChunks.length
}}</strong
><span>{{ chunk.knowledgeName || "未知知识库" }}</span
><span v-if="chunk.knowledgeId"
>知识库ID{{ chunk.knowledgeId }}</span
>
</div>
<div class="chunk-title">{{ chunk.title || "未命名片段" }}</div>
<pre>{{ chunk.content }}</pre>
<a
v-if="chunk.sourceUrl"
:href="chunk.sourceUrl"
target="_blank"
rel="noreferrer"
>查看来源</a
>
</article>
</section>
<el-empty
v-else
description="本条请求未保存召回片段"
:image-size="72"
/>
<h3 class="detail-title">完整 Prompt</h3>
<pre class="prompt-preview">{{
detail.prompt || "无 Prompt 记录"
}}</pre>
</template>
</div>
</el-drawer>
</template>