bugfix: 一些bug修复

This commit is contained in:
2026-05-22 17:45:58 +08:00
parent c286db68b1
commit b02f15f735
12 changed files with 115 additions and 304 deletions
+2 -5
View File
@@ -41,7 +41,7 @@ const pageCount = computed(() => Math.ceil(props.total / props.pageSize));
@update:page="(val: number) => emits('update:page', val)"
>
<PaginationContent v-slot="{ items }">
<PaginationPrevious class="w-5.5 h-5.5 text-primary!">
<PaginationPrevious class="w-5.5 h-5.5">
<ChevronLeftIcon class="size-4" />
</PaginationPrevious>
<template v-for="(item, index) in items" :key="index">
@@ -50,9 +50,6 @@ const pageCount = computed(() => Math.ceil(props.total / props.pageSize));
:value="item.value"
:is-active="item.value === page"
class="w-fit min-w-5.5 h-5.5 text-xs px-1 text-muted-foreground"
:class="{
'text-primary! border-primary!': item.value === page
}"
>
{{ item.value }}
</PaginationItem>
@@ -69,7 +66,7 @@ const pageCount = computed(() => Math.ceil(props.total / props.pageSize));
</Button>
</PaginationEllipsis>
</template>
<PaginationNext class="w-5.5 h-5.5 text-primary!">
<PaginationNext class="w-5.5 h-5.5">
<ChevronRightIcon class="size-4" />
</PaginationNext>
</PaginationContent>
-132
View File
@@ -1,132 +0,0 @@
<!-- app/components/docs/ApiDocsContent.vue - 前端文档页内容覆盖旧 Flask /docs 页面 -->
<script lang="ts" setup>
import { Badge } from "@/components/ui/badge";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle
} from "@/components/ui/card";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow
} from "@/components/ui/table";
/** 文档示例保持本地接口字段名,避免把真实 token 或 key 写进前端 */
const searchExample = `GET /api/search?title=中国的首都是哪个城市?&type=single&options=A. 北京\\nB. 上海`;
const successExample = `{
"code": 1,
"question": "中国的首都是哪个城市?",
"answer": "A"
}`;
const endpoints = [
{
method: "GET/POST",
path: "/api/search",
auth: "可选",
description: "OCS AnswererWrapper 查询入口"
},
{
method: "GET",
path: "/api/health",
auth: "否",
description: "服务健康检查"
},
{
method: "GET",
path: "/api/stats",
auth: "可选",
description: "运行统计和缓存数量"
},
{
method: "GET",
path: "/api/records",
auth: "可选",
description: "最近问答记录"
},
{
method: "POST",
path: "/api/cache/clear",
auth: "可选",
description: "清空当前进程内缓存"
}
];
</script>
<template>
<article class="grid gap-5">
<Card>
<CardHeader>
<CardTitle class="text-xl">API 文档</CardTitle>
<CardDescription class="leading-6">
响应保持 OCS AnswererWrapper 兼容成功返回
<Badge variant="secondary">code: 1</Badge>
失败返回
<Badge variant="secondary">code: 0</Badge>
</CardDescription>
</CardHeader>
</Card>
<Card>
<CardHeader>
<CardTitle>接口列表</CardTitle>
</CardHeader>
<CardContent>
<div class="overflow-auto">
<Table class="min-w-[720px]">
<TableHeader>
<TableRow>
<TableHead>方法</TableHead>
<TableHead>路径</TableHead>
<TableHead>Token</TableHead>
<TableHead>说明</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow v-for="item in endpoints" :key="item.path">
<TableCell>{{ item.method }}</TableCell>
<TableCell class="font-mono text-xs text-foreground">
{{ item.path }}
</TableCell>
<TableCell>{{ item.auth }}</TableCell>
<TableCell>{{ item.description }}</TableCell>
</TableRow>
</TableBody>
</Table>
</div>
</CardContent>
</Card>
<section class="grid gap-4 lg:grid-cols-2">
<Card>
<CardHeader>
<CardTitle>请求示例</CardTitle>
</CardHeader>
<CardContent>
<pre
class="overflow-auto rounded-2xl bg-muted p-4 text-xs leading-5 text-muted-foreground"
><code>{{ searchExample }}</code></pre>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>成功响应</CardTitle>
</CardHeader>
<CardContent>
<pre
class="overflow-auto rounded-2xl bg-muted p-4 text-xs leading-5 text-muted-foreground"
><code>{{ successExample }}</code></pre>
</CardContent>
</Card>
</section>
</article>
</template>
+20 -11
View File
@@ -1,14 +1,17 @@
<!-- app/components/layout/AppHeader.vue - 顶部导航和页面切换入口 -->
<script lang="ts" setup>
import {
Activity,
BookOpenText,
LayoutDashboard,
Search
} from "lucide-vue-next";
import { Activity, LayoutDashboard, Moon, Search, Sun } from "lucide-vue-next";
import { Button } from "@/components/ui/button";
/** 使用 VueUse 的 useColorMode 切换 dark/lightclass 会挂到 <html> 上 */
const colorMode = useColorMode();
const isDark = computed(() => colorMode.value === "dark");
function toggleColorMode() {
colorMode.value = isDark.value ? "light" : "dark";
}
/** 顶部导航项,icon 使用 lucide,和项目里 shadcn 的图标体系保持一致 */
const navItems = [
{
@@ -20,11 +23,6 @@ const navItems = [
label: "Dashboard",
to: "/dashboard",
icon: LayoutDashboard
},
{
label: "文档",
to: "/docs",
icon: BookOpenText
}
];
@@ -73,6 +71,17 @@ const isActive = (to: string) => {
<span>{{ item.label }}</span>
</NuxtLink>
</Button>
<!-- 深色/浅色主题切换按钮 -->
<Button
variant="ghost"
size="icon"
aria-label="切换主题"
@click="toggleColorMode"
>
<Sun v-if="isDark" class="size-4" />
<Moon v-else class="size-4" />
</Button>
</nav>
</div>
</header>
+15 -1
View File
@@ -85,14 +85,28 @@ export interface IQaRecord {
answer: string;
}
/** 最近问答记录分页请求 */
export interface IRecordsRequest {
/** 当前页码,从 1 开始 */
page: number;
/** 每页数量 */
size: number;
}
/** 最近问答记录接口响应 */
export interface IRecordsResponse {
/** 管理接口是否成功 */
success: boolean;
/** 失败时的本地安全文案 */
message?: string;
/** 当前进程内最近问答记录,最新在前 */
/** 当前问答记录,最新在前 */
records: IQaRecord[];
/** 当前页码,从 1 开始 */
page: number;
/** 每页数量 */
size: number;
/** 当前进程内问答记录总数 */
total: number;
}
/** 清空缓存接口响应 */
-12
View File
@@ -1,12 +0,0 @@
<!-- app/pages/docs.vue - API 文档页替代旧 Flask /docs 模板 -->
<script lang="ts" setup>
import ApiDocsContent from "@/components/docs/ApiDocsContent.vue";
useHead({
title: "API 文档 - OCS AI 答题服务"
});
</script>
<template>
<ApiDocsContent />
</template>
+7 -2
View File
@@ -2,6 +2,7 @@
import type {
IClearCacheResponse,
IHealthResponse,
IRecordsRequest,
IRecordsResponse,
ISearchRequest,
ISearchResponse,
@@ -39,9 +40,13 @@ export class AnswerService {
}
/** 读取最近问答记录,供 Dashboard 表格展示 */
public static getRecords() {
public static getRecords(request: IRecordsRequest) {
return BaseClientService.get<IRecordsResponse>(
`${AnswerService.basePath}/records`
`${AnswerService.basePath}/records`,
{
page: request.page,
size: request.size
}
);
}
+19 -22
View File
@@ -82,16 +82,11 @@ export const useAnswerStore = defineStore("answer", () => {
const cacheClearing = ref(false);
const recordsPage = ref(1);
const recordsPageSize = ref(DASHBOARD_RECORD_PAGE_SIZE);
const recordCount = ref(0);
const selectedRecord = ref<IQaRecord>();
/** 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 pagedRecords = computed(() => records.value);
/** Dashboard 顶部运行时长展示 */
const uptimeText = computed(() => {
@@ -103,9 +98,10 @@ export const useAnswerStore = defineStore("answer", () => {
selectedRecord.value = record;
};
/** 切换最近记录表格页码 */
const setRecordsPage = (page: number) => {
recordsPage.value = page;
/** 切换最近记录表格页码,并请求对应页数据 */
const setRecordsPage = async (page: number) => {
if (recordsLoading.value || page === recordsPage.value) return;
await getRecords(page);
};
/** 调用答题 API,返回 OCS 兼容结构但只在 store 中维护 UI 状态 */
@@ -131,6 +127,7 @@ export const useAnswerStore = defineStore("answer", () => {
if (res.data.code === 1) {
answerResult.value = res.data;
recordsPage.value = 1;
await Promise.allSettled([getStats(), getRecords()]);
return;
}
@@ -172,18 +169,18 @@ export const useAnswerStore = defineStore("answer", () => {
}
};
/** 读取最近问答记录,并在数量变少时把页码拉回合法范围 */
const getRecords = async () => {
/** 读取最近问答记录;服务端按 page/size 返回当前页数据 */
const getRecords = async (page = recordsPage.value) => {
recordsLoading.value = true;
try {
const res = await AnswerService.getRecords();
const res = await AnswerService.getRecords({
page,
size: recordsPageSize.value
});
records.value = res.data.records;
const maxPage = Math.max(
1,
Math.ceil(records.value.length / recordsPageSize.value)
);
if (recordsPage.value > maxPage) recordsPage.value = maxPage;
recordsPage.value = res.data.page;
recordsPageSize.value = res.data.size;
recordCount.value = res.data.total;
} catch (error) {
dashboardErrorMessage.value = getClientErrorMessage(
error,
@@ -200,7 +197,7 @@ export const useAnswerStore = defineStore("answer", () => {
await Promise.allSettled([getHealth(), getStats(), getRecords()]);
};
/** 清空缓存后刷新统计;问答记录不会被清空,保持旧服务语义 */
/** 清空缓存和问答记录后刷新统计与表格 */
const clearCache = async () => {
cacheClearing.value = true;
dashboardErrorMessage.value = "";
@@ -210,7 +207,7 @@ export const useAnswerStore = defineStore("answer", () => {
if (!res.data.success) {
dashboardErrorMessage.value = res.data.message;
}
await getStats();
await Promise.allSettled([getStats(), getRecords(1)]);
} catch (error) {
dashboardErrorMessage.value = getClientErrorMessage(
error,