From 0f55de3e9bdf4a996605a12c70fbe5ba0746870d Mon Sep 17 00:00:00 2001 From: nanako <469449812@qq.com> Date: Tue, 11 Nov 2025 14:22:45 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=97=A5=E5=BF=97=E8=AF=A6?= =?UTF-8?q?=E7=BB=86=E9=A1=B5=E9=9D=A2=EF=BC=8C=E6=96=B0=E5=A2=9Ejson?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E6=9F=A5=E7=9C=8B=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/models/schema.go | 22 ++--- frontend/src/components/ui/JsonViewer.css | 69 +++++++++++++++ frontend/src/components/ui/JsonViewer.jsx | 84 ++++++++++++++++++ .../logs/components/RequestLogDetailModal.jsx | 86 ++++++++++--------- .../logs/components/RequestLogList.jsx | 16 ++-- 5 files changed, 217 insertions(+), 60 deletions(-) create mode 100644 frontend/src/components/ui/JsonViewer.css create mode 100644 frontend/src/components/ui/JsonViewer.jsx diff --git a/backend/internal/models/schema.go b/backend/internal/models/schema.go index 0a7064e..4ef919a 100644 --- a/backend/internal/models/schema.go +++ b/backend/internal/models/schema.go @@ -53,15 +53,15 @@ type BackendModel struct { // RequestLog 记录每次API请求的详细信息 type RequestLog struct { gorm.Model - APIKeyID uint `gorm:"index"` // API密钥ID - ProviderName string `gorm:"index"` // 服务商名称 - VirtualModelName string `gorm:"index"` // 虚拟模型名称 - BackendModelName string `gorm:"index"` // 后端模型名称 - RequestTimestamp time.Time `gorm:"index;not null"` // 请求时间戳 - ResponseTimestamp time.Time `gorm:"not null"` // 响应时间戳 - RequestTokens int `gorm:"default:0"` // 请求token数 - ResponseTokens int `gorm:"default:0"` // 响应token数 - Cost float64 `gorm:"type:decimal(10,6)"` // 成本 - RequestBody string `gorm:"type:text"` // 请求体 - ResponseBody string `gorm:"type:text"` // 响应体 + APIKeyID uint `gorm:"index" json:"api_key_id"` // API密钥ID + ProviderName string `gorm:"index" json:"provider_name"` // 服务商名称 + VirtualModelName string `gorm:"index" json:"virtual_model_name"` // 虚拟模型名称 + BackendModelName string `gorm:"index" json:"backend_model_name"` // 后端模型名称 + RequestTimestamp time.Time `gorm:"index;not null" json:"request_timestamp"` // 请求时间戳 + ResponseTimestamp time.Time `gorm:"not null" json:"response_timestamp"` // 响应时间戳 + RequestTokens int `gorm:"default:0" json:"request_tokens"` // 请求token数 + ResponseTokens int `gorm:"default:0" json:"response_tokens"` // 响应token数 + Cost float64 `gorm:"type:decimal(10,6)" json:"cost"` // 成本 + RequestBody string `gorm:"type:text" json:"request_body"` // 请求体 + ResponseBody string `gorm:"type:text" json:"response_body"` // 响应体 } diff --git a/frontend/src/components/ui/JsonViewer.css b/frontend/src/components/ui/JsonViewer.css new file mode 100644 index 0000000..5563ebb --- /dev/null +++ b/frontend/src/components/ui/JsonViewer.css @@ -0,0 +1,69 @@ +/* JsonViewer 自定义样式 */ +/* 针对 dark 主题的样式覆盖 */ + +.json-viewer-dark ._1MGIk { + color: rgb(253, 246, 227) !important; +} + +.json-viewer-dark ._3eOF8 { + color: rgb(253, 246, 227) !important; +} + +.json-viewer-dark ._3uHL6 { + color: rgb(253, 246, 227) !important; +} + +.json-viewer-dark ._1Gho6 { + color: rgb(129, 181, 172) !important; +} + +.json-viewer-dark ._2T6PJ { + color: rgb(129, 181, 172) !important; +} + +.json-viewer-dark ._vGjyY { + color: rgb(203, 75, 22) !important; +} + +.json-viewer-dark ._1bQdo { + color: rgb(211, 54, 130) !important; +} + +.json-viewer-dark ._3zQKs { + color: rgb(174, 129, 255) !important; +} + +.json-viewer-dark ._1xvuR { + color: rgb(38, 139, 210) !important; +} + +.json-viewer-dark ._oLqym { + color: rgb(253, 246, 227) !important; +} + +.json-viewer-dark ._2AXVT { + color: rgb(253, 246, 227) !important; +} + +.json-viewer-dark ._2KJWg { + color: rgb(253, 246, 227) !important; +} + +.json-viewer-dark ._2bSDX { + color: rgb(253, 246, 227) !important; +} + +.json-viewer-dark ._gsbQL { + color: rgb(253, 246, 227) !important; +} + +/* 确保背景色在 dark 主题下正确 */ +.json-viewer-dark { + background-color: transparent !important; +} + +/* 确保整体文本颜色在 dark 主题下正确 */ +.json-viewer-dark, +.json-viewer-dark * { + color-scheme: dark; +} \ No newline at end of file diff --git a/frontend/src/components/ui/JsonViewer.jsx b/frontend/src/components/ui/JsonViewer.jsx new file mode 100644 index 0000000..1469d70 --- /dev/null +++ b/frontend/src/components/ui/JsonViewer.jsx @@ -0,0 +1,84 @@ +import React, { useMemo } from 'react'; +import { JsonView } from 'react-json-view-lite'; +import 'react-json-view-lite/dist/index.css'; +import './JsonViewer.css'; + +const JsonViewer = ({ + data, + theme = 'dark', + collapsed = 1, + collapseAll = true, + enableClipboard = false, + className = '', + style = {}, + fallbackMessage = '无内容' +}) => { + // 根据主题设置容器类名 + const themeClass = theme === 'dark' ? 'json-viewer-dark' : ''; + // 判断数据是否为有效的JSON字符串或对象 + const { isValid, parsedData } = useMemo(() => { + if (!data) { + return { isValid: false, parsedData: null }; + } + + // 如果已经是对象,直接使用 + if (typeof data === 'object' && data !== null) { + return { isValid: true, parsedData: data }; + } + + // 如果是字符串,尝试解析 + if (typeof data === 'string') { + const trimmed = data.trim(); + if (trimmed === '') { + return { isValid: false, parsedData: null }; + } + + try { + const parsed = JSON.parse(trimmed); + return { isValid: true, parsedData: parsed }; + } catch (e) { + return { isValid: false, parsedData: data }; + } + } + + return { isValid: false, parsedData: data }; + }, [data]); + + // 默认样式设置 + const defaultStyle = { + fontFamily: "'Courier New', monospace", + fontSize: '0.875rem', + color: theme === 'dark' ? 'white' : 'black', + ...style + }; + + // 如果是有效的JSON,使用JsonView显示 + if (isValid && parsedData !== null) { + return ( +
+ {parsedData || fallbackMessage}
+
+
- {loading ? '加载中...' : formatJSON(displayLog.RequestBody)}
-
- 无内容
- ) - } +
- {loading ? '加载中...' : formatJSON(displayLog.ResponseBody)}
-
+