From 8444d6e21a54d342c97325624fa2614ec3dee09e Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Tue, 2 Jun 2026 10:48:21 -0600 Subject: [PATCH] update --- src/oj/class/pk.vue | 149 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 129 insertions(+), 20 deletions(-) diff --git a/src/oj/class/pk.vue b/src/oj/class/pk.vue index 17cfbdc..dee4de2 100644 --- a/src/oj/class/pk.vue +++ b/src/oj/class/pk.vue @@ -6,6 +6,10 @@ import { useConfigStore } from "shared/store/config" import { Icon } from "@iconify/vue" import { Bar, Radar } from "vue-chartjs" import { useBreakpoints } from "shared/composables/breakpoints" +import { MdPreview } from "md-editor-v3" +import "md-editor-v3/lib/preview.css" +import { consumeJSONEventStream } from "utils/stream" +import { getCSRFToken } from "utils/functions" import { Chart as ChartJS, CategoryScale, @@ -72,6 +76,11 @@ const duration = ref("") const loading = ref(false) const hasTimeRange = ref(false) +const aiLoading = ref(false) +const aiContent = ref("") +const showAIModal = ref(false) +let aiController: AbortController | null = null + // 时间段选项(与 rank/list.vue 保持一致) const timeRangeOptions: SelectOption[] = [ { label: "全部时间", value: "" }, @@ -145,6 +154,68 @@ async function compare() { } } +async function analyzeWithAI() { + if (aiController) { + aiController.abort() + } + const controller = new AbortController() + aiController = controller + + const timeRangeLabel = + timeRangeOptions.find((o) => o.value === duration.value)?.label ?? "全部时间" + + showAIModal.value = true + aiContent.value = "" + aiLoading.value = true + + const headers: Record = { "Content-Type": "application/json" } + const csrfToken = getCSRFToken() + if (csrfToken) headers["X-CSRFToken"] = csrfToken + + try { + const response = await fetch("/api/ai/class_pk", { + method: "POST", + headers, + body: JSON.stringify({ + comparisons: comparisons.value, + time_range_label: timeRangeLabel, + }), + signal: controller.signal, + }) + + if (!response.ok) throw new Error("AI 分析生成失败") + + let hasStarted = false + + await consumeJSONEventStream(response, { + signal: controller.signal, + onEvent(event) { + if (event === "end" && !hasStarted) aiLoading.value = false + }, + onMessage(payload) { + const parsed = payload as { type?: string; content?: string; message?: string } + if (parsed.type === "delta" && parsed.content) { + if (!hasStarted) { + hasStarted = true + aiLoading.value = false + } + aiContent.value += parsed.content + } else if (parsed.type === "error") { + throw new Error(parsed.message || "AI 服务异常") + } else if (parsed.type === "done" && !hasStarted) { + aiLoading.value = false + } + }, + }) + } catch (error: any) { + if (controller.signal.aborted) return + message.error(error?.message || "AI 分析失败,请稍后再试") + aiLoading.value = false + } finally { + if (aiController === controller) aiController = null + } +} + // 计算排名颜色 function getRankColor(index: number) { if (index === 0) return { type: "success" as const, text: "1" } @@ -566,8 +637,41 @@ const radarChartOptions = { > 开始PK + + + AI分析 + + + +
+ + + + +
+
+
+