feat: 日志列表
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
import type { Group, LogFilter, LogsResponse } from "@/types/models";
|
||||
import type { ApiResponse, Group, LogFilter, LogsResponse } from "@/types/models";
|
||||
import http from "@/utils/http";
|
||||
|
||||
export const logApi = {
|
||||
// 获取日志列表
|
||||
getLogs: (params: LogFilter): Promise<LogsResponse> => {
|
||||
getLogs: (params: LogFilter): Promise<ApiResponse<LogsResponse>> => {
|
||||
return http.get("/logs", { params });
|
||||
},
|
||||
|
||||
// 获取分组列表(用于筛选)
|
||||
getGroups: (): Promise<Group[]> => {
|
||||
getGroups: (): Promise<ApiResponse<Group[]>> => {
|
||||
return http.get("/groups");
|
||||
},
|
||||
};
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { keysApi } from "@/api/keys";
|
||||
import type { APIKey, Group, KeyStatus } from "@/types/models";
|
||||
import { getGroupDisplayName } from "@/utils/display";
|
||||
import { getGroupDisplayName, maskKey } from "@/utils/display";
|
||||
import {
|
||||
AddCircleOutline,
|
||||
AlertCircleOutline,
|
||||
@@ -142,13 +142,6 @@ async function loadKeys() {
|
||||
}
|
||||
}
|
||||
|
||||
function maskKey(key: string): string {
|
||||
if (key.length <= 8) {
|
||||
return key;
|
||||
}
|
||||
return `${key.substring(0, 4)}...${key.substring(key.length - 4)}`;
|
||||
}
|
||||
|
||||
function copyKey(key: KeyRow) {
|
||||
navigator.clipboard
|
||||
.writeText(key.key_value)
|
||||
|
@@ -1,3 +1,10 @@
|
||||
// 通用 API 响应结构
|
||||
export interface ApiResponse<T> {
|
||||
code: number;
|
||||
message: string;
|
||||
data: T;
|
||||
}
|
||||
|
||||
// 密钥状态
|
||||
export type KeyStatus = "active" | "invalid" | undefined;
|
||||
|
||||
@@ -69,32 +76,47 @@ export interface TaskInfo {
|
||||
};
|
||||
}
|
||||
|
||||
// Based on backend response
|
||||
export interface RequestLog {
|
||||
id: string;
|
||||
timestamp: string;
|
||||
group_id: number;
|
||||
key_id: number;
|
||||
is_success: boolean;
|
||||
source_ip: string;
|
||||
status_code: number;
|
||||
request_path: string;
|
||||
request_body_snippet: string;
|
||||
duration_ms: number;
|
||||
error_message: string;
|
||||
user_agent: string;
|
||||
retries: number;
|
||||
group_name?: string;
|
||||
key_value?: string;
|
||||
}
|
||||
|
||||
export interface Pagination {
|
||||
page: number;
|
||||
page_size: number;
|
||||
total_items: number;
|
||||
total_pages: number;
|
||||
}
|
||||
|
||||
export interface LogsResponse {
|
||||
total: number;
|
||||
page: number;
|
||||
size: number;
|
||||
data: RequestLog[];
|
||||
items: RequestLog[];
|
||||
pagination: Pagination;
|
||||
}
|
||||
|
||||
export interface LogFilter {
|
||||
page: number;
|
||||
size: number;
|
||||
group_id?: number;
|
||||
start_time?: string;
|
||||
end_time?: string;
|
||||
status_code?: number;
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
group_name?: string;
|
||||
key_value?: string;
|
||||
is_success?: boolean | null;
|
||||
status_code?: number | null;
|
||||
source_ip?: string;
|
||||
error_contains?: string;
|
||||
start_time?: string | null;
|
||||
end_time?: string | null;
|
||||
}
|
||||
|
||||
export interface DashboardStats {
|
||||
|
@@ -37,3 +37,15 @@ export function formatDisplayName(name: string): string {
|
||||
export function getGroupDisplayName(group: Group): string {
|
||||
return group.display_name || formatDisplayName(group.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Masks a long key string for display.
|
||||
* @param key The key string.
|
||||
* @returns The masked key.
|
||||
*/
|
||||
export function maskKey(key: string): string {
|
||||
if (!key || key.length <= 8) {
|
||||
return key || "";
|
||||
}
|
||||
return `${key.substring(0, 4)}...${key.substring(key.length - 4)}`;
|
||||
}
|
||||
|
@@ -1,102 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { NCard, NH3, NSpace, NTag, NText } from "naive-ui";
|
||||
// 这里可以添加日志管理相关的逻辑
|
||||
import LogTable from "@/components/logs/LogTable.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="logs-container">
|
||||
<n-space vertical size="large">
|
||||
<!-- 占位符内容 -->
|
||||
<div class="content-placeholder">
|
||||
<n-card :bordered="false" class="placeholder-card">
|
||||
<n-space vertical align="center" size="large">
|
||||
<div class="placeholder-icon">📋</div>
|
||||
<n-h3 class="placeholder-title">日志管理功能</n-h3>
|
||||
<n-text depth="2" class="placeholder-description">
|
||||
此功能正在开发中,将提供完整的系统日志查看和管理功能,包括实时日志、历史记录和日志分析。
|
||||
</n-text>
|
||||
|
||||
<n-space wrap size="medium" class="placeholder-features">
|
||||
<n-tag type="info" size="medium">📝 实时日志流</n-tag>
|
||||
<n-tag type="info" size="medium">🔍 日志搜索过滤</n-tag>
|
||||
<n-tag type="info" size="medium">📈 错误统计分析</n-tag>
|
||||
<n-tag type="info" size="medium">💾 日志导出功能</n-tag>
|
||||
</n-space>
|
||||
</n-space>
|
||||
</n-card>
|
||||
</div>
|
||||
</n-space>
|
||||
<div>
|
||||
<log-table />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.page-header-card {
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
border-radius: var(--border-radius-lg);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
animation: fadeInUp 0.2s ease-out;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 2.25rem;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.content-placeholder {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 400px;
|
||||
animation: fadeInUp 0.2s ease-out 0.1s both;
|
||||
}
|
||||
|
||||
.placeholder-card {
|
||||
text-align: center;
|
||||
max-width: 500px;
|
||||
padding: 48px 32px;
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
border-radius: var(--border-radius-lg);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.placeholder-icon {
|
||||
font-size: 4rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.placeholder-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.placeholder-description {
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
text-align: center;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.placeholder-features {
|
||||
justify-content: center;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
Reference in New Issue
Block a user