From 008eb14adcc36df27b7742c6a160e9b872e7e261 Mon Sep 17 00:00:00 2001 From: skycurtain Date: Fri, 9 Oct 2026 01:41:45 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20=E7=AE=A1=E7=90=86=E9=9D=A2?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E6=97=A5=E5=BF=97=E2=80=94=E2=80=94=E7=AD=9B?= =?UTF-8?q?=E9=80=89=E6=98=8E=E7=BB=86=E4=B8=8E=20key/=E6=88=90=E5=91=98?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC=E9=93=BE=E8=B7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/server/src/routes/admin-usage.ts | 17 +- apps/web/src/api/admin/logs.ts | 16 +- apps/web/src/api/query-keys.ts | 16 +- apps/web/src/layouts/admin-layout.vue | 2 + apps/web/src/queries/admin.ts | 4 +- apps/web/src/router/admin.ts | 5 + apps/web/src/views/admin/keys-view.vue | 8 + apps/web/src/views/admin/logs-view.vue | 328 +++++++++++++++++++++ apps/web/src/views/admin/members-view.vue | 20 +- apps/web/src/views/admin/overview-view.vue | 16 +- apps/web/vite-dev.err | 1 + packages/db/src/repos.ts | 20 +- 12 files changed, 436 insertions(+), 17 deletions(-) create mode 100644 apps/web/src/views/admin/logs-view.vue create mode 100644 apps/web/vite-dev.err diff --git a/apps/server/src/routes/admin-usage.ts b/apps/server/src/routes/admin-usage.ts index 6ebb33d..a3c072f 100644 --- a/apps/server/src/routes/admin-usage.ts +++ b/apps/server/src/routes/admin-usage.ts @@ -317,12 +317,23 @@ export function adminUsageRoutes() { page, page_size, api_key_id: q.api_key_id ? Number(q.api_key_id) : undefined, - member_id: q.member_id ? Number(q.member_id) : undefined, - billing_source: q.billing_source || undefined, + member_id: + q.member_id === "unassigned" + ? "unassigned" + : q.member_id + ? Number(q.member_id) + : undefined, + billing_source: + q.billing_source === "none" ? "none" : q.billing_source || undefined, subscription_id: q.subscription_id ? Number(q.subscription_id) : undefined, - type: q.type != null && q.type !== "" ? Number(q.type) : undefined, + type: + q.type === "error" + ? "error" + : q.type != null && q.type !== "" + ? Number(q.type) + : undefined, }); const items: LogItem[] = rows.map((row) => { diff --git a/apps/web/src/api/admin/logs.ts b/apps/web/src/api/admin/logs.ts index 1ea9843..bc2149b 100644 --- a/apps/web/src/api/admin/logs.ts +++ b/apps/web/src/api/admin/logs.ts @@ -1,11 +1,23 @@ import type { LogItem, Paginated } from "@new-api-key-usage/core"; import { adminHttp } from "../http.ts"; +export type AdminLogFilters = { + from: string; + to: string; + page: number; + page_size: number; + api_key_id?: number; + member_id?: number | "unassigned"; + billing_source?: string; + subscription_id?: number; + type?: number | "error"; +}; + export async function fetchLogs( - range: { from: string; to: string; page: number; page_size: number }, + params: AdminLogFilters, ): Promise> { const { data } = await adminHttp.get>("/logs", { - params: range, + params, }); return data; } diff --git a/apps/web/src/api/query-keys.ts b/apps/web/src/api/query-keys.ts index 4c079f9..7c701dd 100644 --- a/apps/web/src/api/query-keys.ts +++ b/apps/web/src/api/query-keys.ts @@ -26,8 +26,20 @@ export const qk = { overview: (range: TimeRange) => ["admin", "overview", range.from, range.to] as const, keys: (filters: KeyFilters) => ["admin", "keys", filters] as const, - logs: (range: TimeRange & PageQuery) => - ["admin", "logs", range.from, range.to, range.page, range.page_size] as const, + logs: (range: TimeRange & PageQuery & Record) => + [ + "admin", + "logs", + range.from, + range.to, + range.page, + range.page_size, + range.api_key_id ?? "", + range.member_id ?? "", + range.billing_source ?? "", + range.subscription_id ?? "", + range.type ?? "", + ] as const, payments: () => ["admin", "payments"] as const, qpuLatest: () => ["admin", "qpu-latest"] as const, members: () => ["admin", "members"] as const, diff --git a/apps/web/src/layouts/admin-layout.vue b/apps/web/src/layouts/admin-layout.vue index 192a060..a5e403e 100644 --- a/apps/web/src/layouts/admin-layout.vue +++ b/apps/web/src/layouts/admin-layout.vue @@ -6,6 +6,7 @@ import { AuditOutlined, DashboardOutlined, DownOutlined, + FileTextOutlined, KeyOutlined, LogoutOutlined, MenuFoldOutlined, @@ -42,6 +43,7 @@ const username = computed( /** 收起时仅剩图标——每项必须带 icon(交互共性侧边栏六项) */ const items: MenuItemType[] = [ { key: "/admin/overview", icon: DashboardOutlined, label: "总览" }, + { key: "/admin/logs", icon: FileTextOutlined, label: "调用日志" }, { key: "/admin/keys", icon: KeyOutlined, label: "Key 管理" }, { key: "/admin/payments", icon: AccountBookOutlined, label: "支付记录" }, { key: "/admin/members", icon: TeamOutlined, label: "成员管理" }, diff --git a/apps/web/src/queries/admin.ts b/apps/web/src/queries/admin.ts index 2b56e12..41f94a2 100644 --- a/apps/web/src/queries/admin.ts +++ b/apps/web/src/queries/admin.ts @@ -85,7 +85,9 @@ export function usePatchKey() { }); } -export function useAdminLogs(params: () => TimeRange & PageQuery) { +export function useAdminLogs( + params: () => Parameters[0], +) { return useQuery({ queryKey: () => qk.admin.logs(params()), queryFn: () => fetchLogs(params()), diff --git a/apps/web/src/router/admin.ts b/apps/web/src/router/admin.ts index e541796..1aa7f02 100644 --- a/apps/web/src/router/admin.ts +++ b/apps/web/src/router/admin.ts @@ -26,6 +26,11 @@ export const adminRoutes: RouteRecordRaw[] = [ name: "admin-overview", component: () => import("@/views/admin/overview-view.vue"), }, + { + path: "logs", + name: "admin-logs", + component: () => import("@/views/admin/logs-view.vue"), + }, { path: "keys", name: "admin-keys", diff --git a/apps/web/src/views/admin/keys-view.vue b/apps/web/src/views/admin/keys-view.vue index 95b85d6..3005b38 100644 --- a/apps/web/src/views/admin/keys-view.vue +++ b/apps/web/src/views/admin/keys-view.vue @@ -297,6 +297,13 @@ function onRowClass(record: KeyItem) { {{ formatMinute(record.registered_at) }} diff --git a/apps/web/src/views/admin/logs-view.vue b/apps/web/src/views/admin/logs-view.vue new file mode 100644 index 0000000..88fb125 --- /dev/null +++ b/apps/web/src/views/admin/logs-view.vue @@ -0,0 +1,328 @@ + + + + + diff --git a/apps/web/src/views/admin/members-view.vue b/apps/web/src/views/admin/members-view.vue index 7750b4b..1b956d2 100644 --- a/apps/web/src/views/admin/members-view.vue +++ b/apps/web/src/views/admin/members-view.vue @@ -5,6 +5,10 @@ import { computed, h, reactive, ref } from "vue"; import { Tooltip, message } from "antdv-next"; import { QuestionCircleOutlined } from "@antdv-next/icons"; +import { useRoute, useRouter } from "vue-router"; + +const route = useRoute(); +const router = useRouter(); import type { MemberItem } from "@new-api-key-usage/core"; import type { ApiError } from "@/api/http.ts"; import PageHeader from "@/components/common/page-header.vue"; @@ -58,7 +62,15 @@ const columns = [ { title: "操作", key: "actions", width: 200 }, ]; -const rows = computed(() => membersQuery.data.value ?? []); +const rows = computed(() => { + const list = membersQuery.data.value ?? []; + const mid = route.query.member_id; + if (typeof mid === "string" && mid) { + const n = Number(mid); + return list.filter((m) => m.id === n); + } + return list; +}); async function onCreate() { createError.value = ""; @@ -156,6 +168,12 @@ async function onToggle(record: MemberItem) {