// app/stores/answer.ts - 答题页和 Dashboard 的统一状态管理 import { defineStore } from "pinia"; import type { IHealthResponse, IQaRecord, ISearchSuccessResponse, IStatsResponse, QuestionType } from "@/interfaces"; import { AnswerService } from "@/services"; /** Dashboard 最近记录表格每页数量,组件直接复用,避免多个地方写死 */ export const DASHBOARD_RECORD_PAGE_SIZE = 10; /** 题型下拉选项,value 保持 OCS / 旧 Python 服务使用的英文值 */ export const QUESTION_TYPE_OPTIONS: Array<{ label: string; value: QuestionType; }> = [ { label: "自动判断", value: "" }, { label: "单选题", value: "single" }, { label: "多选题", value: "multiple" }, { label: "判断题", value: "judgement" }, { label: "填空题", value: "completion" } ]; /** 题型展示文案,Dashboard 表格用它把英文值转成本地业务表达 */ export const QUESTION_TYPE_LABELS: Record = { "": "自动判断", single: "单选题", multiple: "多选题", judgement: "判断题", completion: "填空题" }; /** 从 `$fetch` 错误里提取安全文案;服务端已经保证 message/msg 不含敏感上游细节 */ const getClientErrorMessage = (error: unknown, fallback: string) => { if (typeof error !== "object" || error === null) return fallback; const data = "data" in error ? (error as { data?: unknown }).data : undefined; if (typeof data === "object" && data !== null) { const message = (data as { message?: unknown; msg?: unknown }).message; const msg = (data as { message?: unknown; msg?: unknown }).msg; if (typeof message === "string" && message) return message; if (typeof msg === "string" && msg) return msg; } return fallback; }; /** 把运行秒数格式化为 dashboard 更容易扫读的时长 */ const formatUptime = (seconds: number) => { const safeSeconds = Math.max(0, Math.floor(seconds)); const days = Math.floor(safeSeconds / 86_400); const hours = Math.floor((safeSeconds % 86_400) / 3_600); const minutes = Math.floor((safeSeconds % 3_600) / 60); if (days > 0) return `${days} 天 ${hours} 小时`; if (hours > 0) return `${hours} 小时 ${minutes} 分钟`; return `${Math.max(1, minutes)} 分钟`; }; /** OCS AI 答题服务前端状态管理 */ export const useAnswerStore = defineStore("answer", () => { // ---- 答题表单状态 ---- const question = ref(""); const questionType = ref(""); const options = ref(""); const searchLoading = ref(false); const answerResult = ref(); const searchErrorMessage = ref(""); // ---- Dashboard 状态 ---- const health = ref(); const stats = ref(); const records = ref([]); const dashboardErrorMessage = ref(""); const statsLoading = ref(false); const recordsLoading = ref(false); const cacheClearing = ref(false); const recordsPage = ref(1); const recordsPageSize = ref(DASHBOARD_RECORD_PAGE_SIZE); const selectedRecord = ref(); /** Dashboard 表格当前页数据,分页只作用在前端的最近 100 条内存记录上 */ const pagedRecords = computed(() => { const start = (recordsPage.value - 1) * recordsPageSize.value; return records.value.slice(start, start + recordsPageSize.value); }); /** 表格总数,用于 TableCom 分页器 */ const recordCount = computed(() => records.value.length); /** Dashboard 顶部运行时长展示 */ const uptimeText = computed(() => { return stats.value ? formatUptime(stats.value.uptime) : "-"; }); /** 选择表格记录,交给详情弹窗展示完整题目、选项和答案 */ const selectRecord = (record?: IQaRecord) => { selectedRecord.value = record; }; /** 切换最近记录表格页码 */ const setRecordsPage = (page: number) => { recordsPage.value = page; }; /** 调用答题 API,返回 OCS 兼容结构但只在 store 中维护 UI 状态 */ const searchAnswer = async () => { const title = question.value.trim(); const requestOptions = options.value.trim(); answerResult.value = undefined; searchErrorMessage.value = ""; if (!title) { searchErrorMessage.value = "未提供问题内容"; return; } searchLoading.value = true; try { const res = await AnswerService.search({ title, type: questionType.value, options: requestOptions }); if (res.data.code === 1) { answerResult.value = res.data; await Promise.allSettled([getStats(), getRecords()]); return; } searchErrorMessage.value = res.data.msg || "答题失败"; } catch (error) { searchErrorMessage.value = getClientErrorMessage(error, "服务器内部错误"); } finally { searchLoading.value = false; } }; /** 读取健康检查,失败时只写本地错误文案 */ const getHealth = async () => { try { const res = await AnswerService.getHealth(); health.value = res.data; } catch (error) { dashboardErrorMessage.value = getClientErrorMessage( error, "健康检查失败" ); } }; /** 读取运行统计 */ const getStats = async () => { statsLoading.value = true; try { const res = await AnswerService.getStats(); stats.value = res.data; } catch (error) { dashboardErrorMessage.value = getClientErrorMessage( error, "统计信息读取失败" ); } finally { statsLoading.value = false; } }; /** 读取最近问答记录,并在数量变少时把页码拉回合法范围 */ const getRecords = async () => { recordsLoading.value = true; try { const res = await AnswerService.getRecords(); records.value = res.data.records; const maxPage = Math.max( 1, Math.ceil(records.value.length / recordsPageSize.value) ); if (recordsPage.value > maxPage) recordsPage.value = maxPage; } catch (error) { dashboardErrorMessage.value = getClientErrorMessage( error, "问答记录读取失败" ); } finally { recordsLoading.value = false; } }; /** Dashboard 首次加载时并行拉取运行状态、统计和最近记录 */ const loadDashboard = async () => { dashboardErrorMessage.value = ""; await Promise.allSettled([getHealth(), getStats(), getRecords()]); }; /** 清空缓存后刷新统计;问答记录不会被清空,保持旧服务语义 */ const clearCache = async () => { cacheClearing.value = true; dashboardErrorMessage.value = ""; try { const res = await AnswerService.clearCache(); if (!res.data.success) { dashboardErrorMessage.value = res.data.message; } await getStats(); } catch (error) { dashboardErrorMessage.value = getClientErrorMessage( error, "缓存清理失败" ); } finally { cacheClearing.value = false; } }; return { question, questionType, options, searchLoading, answerResult, searchErrorMessage, health, stats, records, dashboardErrorMessage, statsLoading, recordsLoading, cacheClearing, recordsPage, recordsPageSize, selectedRecord, pagedRecords, recordCount, uptimeText, searchAnswer, getHealth, getStats, getRecords, loadDashboard, clearCache, selectRecord, setRecordsPage }; });