From d6c2bd791960c01b0f77b90731c6cfd9c434f9a9 Mon Sep 17 00:00:00 2001 From: hex2077 Date: Sun, 1 Feb 2026 22:43:28 +0800 Subject: [PATCH] =?UTF-8?q?feat(usage):=20=E5=9C=A8=E7=94=A8=E9=87=8F?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E9=A1=B5=E9=9D=A2=E6=B7=BB=E5=8A=A0=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E7=AB=AF=E6=97=B6=E9=97=B4=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 UI 中添加服务端时间显示组件,包含样式和国际化支持 - 修改用量 API 返回数据,始终包含 serverTime 字段 - 更新前端 JavaScript 以解析并显示服务端时间 - 新增 OpenClaw 配置指南文档(中/英/日文) --- OPENCLAW_CONFIG_GUIDE-JA.md | 213 +++++++++++++++++++++++++++ OPENCLAW_CONFIG_GUIDE-ZH.md | 213 +++++++++++++++++++++++++++ OPENCLAW_CONFIG_GUIDE.md | 213 +++++++++++++++++++++++++++ README-JA.md | 2 +- README-ZH.md | 2 +- README.md | 2 +- src/ui-modules/usage-api.js | 18 ++- static/app/i18n.js | 2 + static/app/usage-manager.js | 16 ++ static/components/section-usage.css | 14 ++ static/components/section-usage.html | 3 + 11 files changed, 692 insertions(+), 6 deletions(-) create mode 100644 OPENCLAW_CONFIG_GUIDE-JA.md create mode 100644 OPENCLAW_CONFIG_GUIDE-ZH.md create mode 100644 OPENCLAW_CONFIG_GUIDE.md diff --git a/OPENCLAW_CONFIG_GUIDE-JA.md b/OPENCLAW_CONFIG_GUIDE-JA.md new file mode 100644 index 0000000..d6afec7 --- /dev/null +++ b/OPENCLAW_CONFIG_GUIDE-JA.md @@ -0,0 +1,213 @@ +# OpenClaw 設定ガイド + +OpenClaw で AIClient-2-API を使用するためのクイック設定ガイド。 + +--- + +## 前提条件 + +1. AIClient-2-API サービスを起動 +2. Web UI (`http://localhost:3000`) で少なくとも1つのプロバイダーを設定 +3. 設定ファイルから API Key を記録 +4. OpenClaw をインストール + - Docker バージョン:[justlikemaki/openclaw-docker-cn-im](https://hub.docker.com/r/justlikemaki/openclaw-docker-cn-im) + - または他のインストール方法を使用 + +--- + +## 設定方法 + +### 方法1:OpenAI プロトコル(推奨) + +**使用例**:Gemini モデルを使用する場合 + +```json5 +{ + env: { + AICLIENT2API_KEY: "your-api-key" + }, + agents: { + defaults: { + model: { primary: "aiclient2api/gemini-3-flash-preview" }, + models: { + "aiclient2api/gemini-3-flash-preview": { alias: "Gemini 3 Flash" } + } + } + }, + models: { + mode: "merge", + providers: { + aiclient2api: { + baseUrl: "http://localhost:3000/v1", + apiKey: "${AICLIENT2API_KEY}", + api: "openai-completions", + models: [ + { + id: "gemini-3-flash-preview", + name: "Gemini 3 Flash Preview", + reasoning: false, + input: ["text", "image"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 1000000, + maxTokens: 8192 + } + ] + } + } + } +} +``` + +### 方法2:Claude プロトコル + +**使用例**:Prompt Caching などの機能を持つ Claude モデルを使用する場合 + +```json5 +{ + env: { + AICLIENT2API_KEY: "your-api-key" + }, + agents: { + defaults: { + model: { primary: "aiclient2api/claude-sonnet-4-5" }, + models: { + "aiclient2api/claude-sonnet-4-5": { alias: "Claude Sonnet 4.5" } + } + } + }, + models: { + mode: "merge", + providers: { + aiclient2api: { + baseUrl: "http://localhost:3000", + apiKey: "${AICLIENT2API_KEY}", + api: "anthropic-messages", + models: [ + { + id: "claude-sonnet-4-5", + name: "Claude Sonnet 4.5", + reasoning: false, + input: ["text", "image"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 200000, + maxTokens: 8192 + } + ] + } + } + } +} +``` + +--- + +## プロバイダーの指定(オプション) + +ルーティングパラメータで特定のプロバイダーを指定: + +```json5 +{ + models: { + providers: { + // Kiro Claude(OpenAI プロトコル) + "aiclient2api-kiro": { + baseUrl: "http://localhost:3000/claude-kiro-oauth/v1", + apiKey: "${AICLIENT2API_KEY}", + api: "openai-completions", + models: [...] + }, + + // Kiro Claude(Claude プロトコル) + "aiclient2api-kiro-claude": { + baseUrl: "http://localhost:3000/claude-kiro-oauth", + apiKey: "${AICLIENT2API_KEY}", + api: "anthropic-messages", + models: [...] + }, + + // Gemini CLI(OpenAI プロトコル) + "aiclient2api-gemini": { + baseUrl: "http://localhost:3000/gemini-cli-oauth/v1", + apiKey: "${AICLIENT2API_KEY}", + api: "openai-completions", + models: [...] + }, + + // Antigravity(OpenAI プロトコル) + "aiclient2api-antigravity": { + baseUrl: "http://localhost:3000/gemini-antigravity/v1", + apiKey: "${AICLIENT2API_KEY}", + api: "openai-completions", + models: [...] + } + } + } +} +``` + +--- + +## フォールバックの設定 + +```json5 +{ + agents: { + defaults: { + model: { + primary: "aiclient2api/claude-sonnet-4-5", + fallbacks: [ + "aiclient2api/gemini-3-flash-preview" + ] + } + } + } +} +``` + +--- + +## よく使うコマンド + +```bash +# すべてのモデルをリスト表示 +openclaw models list + +# モデルを切り替え +openclaw models set aiclient2api/claude-sonnet-4-5 + +# 特定のモデルでチャット +openclaw chat --model aiclient2api/gemini-3-flash-preview "あなたの質問" +``` + +--- + +## プロトコル比較 + +| 機能 | OpenAI プロトコル | Claude プロトコル | +|------|------------------|------------------| +| Base URL | `http://localhost:3000/v1` | `http://localhost:3000` | +| API タイプ | `openai-completions` | `anthropic-messages` | +| サポートモデル | すべてのモデル | Claude のみ | +| 特殊機能 | - | Prompt Caching、Extended Thinking | + +--- + +## よくある質問 + +**Q: 接続に失敗しますか?** +- AIClient-2-API サービスが実行中であることを確認 +- Base URL が正しいか確認(OpenAI プロトコルには `/v1` サフィックスが必要) +- `localhost` の代わりに `127.0.0.1` を使用してみる + +**Q: 401 エラー?** +- API Key が正しく設定されているか確認 +- 環境変数 `AICLIENT2API_KEY` が設定されているか確認 + +**Q: モデルが利用できない?** +- AIClient-2-API Web UI でプロバイダーが設定されているか確認 +- `openclaw gateway restart` を実行してゲートウェイを再起動 +- `openclaw models list` を実行してモデルリストを確認 + +--- + +詳細については、[AIClient-2-API ドキュメント](./README-JA.md) を参照してください diff --git a/OPENCLAW_CONFIG_GUIDE-ZH.md b/OPENCLAW_CONFIG_GUIDE-ZH.md new file mode 100644 index 0000000..e45b8cc --- /dev/null +++ b/OPENCLAW_CONFIG_GUIDE-ZH.md @@ -0,0 +1,213 @@ +# OpenClaw 配置指南 + +在 OpenClaw 中使用 AIClient-2-API 的快速配置指南。 + +--- + +## 前置准备 + +1. 启动 AIClient-2-API 服务 +2. 在 Web UI (`http://localhost:3000`) 配置至少一个提供商 +3. 记录配置文件中的 API Key +4. 安装 OpenClaw + - Docker 版本:[justlikemaki/openclaw-docker-cn-im](https://hub.docker.com/r/justlikemaki/openclaw-docker-cn-im) + - 或使用其他安装方式 + +--- + +## 配置方式 + +### 方式一:OpenAI 协议(推荐) + +**适用场景**:使用 Gemini 模型 + +```json5 +{ + env: { + AICLIENT2API_KEY: "your-api-key" + }, + agents: { + defaults: { + model: { primary: "aiclient2api/gemini-3-flash-preview" }, + models: { + "aiclient2api/gemini-3-flash-preview": { alias: "Gemini 3 Flash" } + } + } + }, + models: { + mode: "merge", + providers: { + aiclient2api: { + baseUrl: "http://localhost:3000/v1", + apiKey: "${AICLIENT2API_KEY}", + api: "openai-completions", + models: [ + { + id: "gemini-3-flash-preview", + name: "Gemini 3 Flash Preview", + reasoning: false, + input: ["text", "image"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 1000000, + maxTokens: 8192 + } + ] + } + } + } +} +``` + +### 方式二:Claude 协议 + +**适用场景**:使用 Claude 模型,需要 Prompt Caching 等特性 + +```json5 +{ + env: { + AICLIENT2API_KEY: "your-api-key" + }, + agents: { + defaults: { + model: { primary: "aiclient2api/claude-sonnet-4-5" }, + models: { + "aiclient2api/claude-sonnet-4-5": { alias: "Claude Sonnet 4.5" } + } + } + }, + models: { + mode: "merge", + providers: { + aiclient2api: { + baseUrl: "http://localhost:3000", + apiKey: "${AICLIENT2API_KEY}", + api: "anthropic-messages", + models: [ + { + id: "claude-sonnet-4-5", + name: "Claude Sonnet 4.5", + reasoning: false, + input: ["text", "image"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 200000, + maxTokens: 8192 + } + ] + } + } + } +} +``` + +--- + +## 指定提供商(可选) + +通过路由参数指定特定提供商: + +```json5 +{ + models: { + providers: { + // Kiro 提供的 Claude (OpenAI 协议) + "aiclient2api-kiro": { + baseUrl: "http://localhost:3000/claude-kiro-oauth/v1", + apiKey: "${AICLIENT2API_KEY}", + api: "openai-completions", + models: [...] + }, + + // Kiro 提供的 Claude (Claude 协议) + "aiclient2api-kiro-claude": { + baseUrl: "http://localhost:3000/claude-kiro-oauth", + apiKey: "${AICLIENT2API_KEY}", + api: "anthropic-messages", + models: [...] + }, + + // Gemini CLI (OpenAI 协议) + "aiclient2api-gemini": { + baseUrl: "http://localhost:3000/gemini-cli-oauth/v1", + apiKey: "${AICLIENT2API_KEY}", + api: "openai-completions", + models: [...] + }, + + // Antigravity (OpenAI 协议) + "aiclient2api-antigravity": { + baseUrl: "http://localhost:3000/gemini-antigravity/v1", + apiKey: "${AICLIENT2API_KEY}", + api: "openai-completions", + models: [...] + } + } + } +} +``` + +--- + +## 配置 Fallback + +```json5 +{ + agents: { + defaults: { + model: { + primary: "aiclient2api/claude-sonnet-4-5", + fallbacks: [ + "aiclient2api/gemini-3-flash-preview" + ] + } + } + } +} +``` + +--- + +## 常用命令 + +```bash +# 列出所有模型 +openclaw models list + +# 切换模型 +openclaw models set aiclient2api/claude-sonnet-4-5 + +# 使用指定模型对话 +openclaw chat --model aiclient2api/gemini-3-flash-preview "你的问题" +``` + +--- + +## 协议对比 + +| 特性 | OpenAI 协议 | Claude 协议 | +|------|------------|------------| +| Base URL | `http://localhost:3000/v1` | `http://localhost:3000` | +| API 类型 | `openai-completions` | `anthropic-messages` | +| 支持模型 | 所有模型 | 仅 Claude | +| 特殊特性 | - | Prompt Caching、Extended Thinking | + +--- + +## 常见问题 + +**Q: 连接失败?** +- 确认 AIClient-2-API 服务运行中 +- 检查 Base URL 是否正确(OpenAI 协议需要 `/v1` 后缀) +- 尝试使用 `127.0.0.1` 替代 `localhost` + +**Q: 401 错误?** +- 检查 API Key 是否正确配置 +- 确认环境变量 `AICLIENT2API_KEY` 已设置 + +**Q: 模型不可用?** +- 在 AIClient-2-API Web UI 确认已配置对应提供商 +- 运行 `openclaw gateway restart` 重启网关 +- 运行 `openclaw models list` 验证模型列表 + +--- + +更多信息请参考 [AIClient-2-API 文档](../README-ZH.md) diff --git a/OPENCLAW_CONFIG_GUIDE.md b/OPENCLAW_CONFIG_GUIDE.md new file mode 100644 index 0000000..ff6fe25 --- /dev/null +++ b/OPENCLAW_CONFIG_GUIDE.md @@ -0,0 +1,213 @@ +# OpenClaw Configuration Guide + +Quick configuration guide for using AIClient-2-API with OpenClaw. + +--- + +## Prerequisites + +1. Start AIClient-2-API service +2. Configure at least one provider in Web UI (`http://localhost:3000`) +3. Note the API Key from configuration file +4. Install OpenClaw + - Docker version: [justlikemaki/openclaw-docker-cn-im](https://hub.docker.com/r/justlikemaki/openclaw-docker-cn-im) + - Or use other installation methods + +--- + +## Configuration Methods + +### Method 1: OpenAI Protocol (Recommended) + +**Use Case**: For Gemini models + +```json5 +{ + env: { + AICLIENT2API_KEY: "your-api-key" + }, + agents: { + defaults: { + model: { primary: "aiclient2api/gemini-3-flash-preview" }, + models: { + "aiclient2api/gemini-3-flash-preview": { alias: "Gemini 3 Flash" } + } + } + }, + models: { + mode: "merge", + providers: { + aiclient2api: { + baseUrl: "http://localhost:3000/v1", + apiKey: "${AICLIENT2API_KEY}", + api: "openai-completions", + models: [ + { + id: "gemini-3-flash-preview", + name: "Gemini 3 Flash Preview", + reasoning: false, + input: ["text", "image"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 1000000, + maxTokens: 8192 + } + ] + } + } + } +} +``` + +### Method 2: Claude Protocol + +**Use Case**: For Claude models with features like Prompt Caching + +```json5 +{ + env: { + AICLIENT2API_KEY: "your-api-key" + }, + agents: { + defaults: { + model: { primary: "aiclient2api/claude-sonnet-4-5" }, + models: { + "aiclient2api/claude-sonnet-4-5": { alias: "Claude Sonnet 4.5" } + } + } + }, + models: { + mode: "merge", + providers: { + aiclient2api: { + baseUrl: "http://localhost:3000", + apiKey: "${AICLIENT2API_KEY}", + api: "anthropic-messages", + models: [ + { + id: "claude-sonnet-4-5", + name: "Claude Sonnet 4.5", + reasoning: false, + input: ["text", "image"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 200000, + maxTokens: 8192 + } + ] + } + } + } +} +``` + +--- + +## Specify Provider (Optional) + +Specify a specific provider via routing parameters: + +```json5 +{ + models: { + providers: { + // Kiro Claude (OpenAI Protocol) + "aiclient2api-kiro": { + baseUrl: "http://localhost:3000/claude-kiro-oauth/v1", + apiKey: "${AICLIENT2API_KEY}", + api: "openai-completions", + models: [...] + }, + + // Kiro Claude (Claude Protocol) + "aiclient2api-kiro-claude": { + baseUrl: "http://localhost:3000/claude-kiro-oauth", + apiKey: "${AICLIENT2API_KEY}", + api: "anthropic-messages", + models: [...] + }, + + // Gemini CLI (OpenAI Protocol) + "aiclient2api-gemini": { + baseUrl: "http://localhost:3000/gemini-cli-oauth/v1", + apiKey: "${AICLIENT2API_KEY}", + api: "openai-completions", + models: [...] + }, + + // Antigravity (OpenAI Protocol) + "aiclient2api-antigravity": { + baseUrl: "http://localhost:3000/gemini-antigravity/v1", + apiKey: "${AICLIENT2API_KEY}", + api: "openai-completions", + models: [...] + } + } + } +} +``` + +--- + +## Configure Fallback + +```json5 +{ + agents: { + defaults: { + model: { + primary: "aiclient2api/claude-sonnet-4-5", + fallbacks: [ + "aiclient2api/gemini-3-flash-preview" + ] + } + } + } +} +``` + +--- + +## Common Commands + +```bash +# List all models +openclaw models list + +# Switch model +openclaw models set aiclient2api/claude-sonnet-4-5 + +# Chat with specific model +openclaw chat --model aiclient2api/gemini-3-flash-preview "your question" +``` + +--- + +## Protocol Comparison + +| Feature | OpenAI Protocol | Claude Protocol | +|---------|----------------|-----------------| +| Base URL | `http://localhost:3000/v1` | `http://localhost:3000` | +| API Type | `openai-completions` | `anthropic-messages` | +| Supported Models | All models | Claude only | +| Special Features | - | Prompt Caching, Extended Thinking | + +--- + +## FAQ + +**Q: Connection failed?** +- Confirm AIClient-2-API service is running +- Check if Base URL is correct (OpenAI protocol needs `/v1` suffix) +- Try using `127.0.0.1` instead of `localhost` + +**Q: 401 error?** +- Check if API Key is correctly configured +- Confirm environment variable `AICLIENT2API_KEY` is set + +**Q: Model unavailable?** +- Confirm provider is configured in AIClient-2-API Web UI +- Run `openclaw gateway restart` to restart gateway +- Run `openclaw models list` to verify model list + +--- + +For more information, see [AIClient-2-API Documentation](./README.md) diff --git a/README-JA.md b/README-JA.md index ff70e4c..970cb55 100644 --- a/README-JA.md +++ b/README-JA.md @@ -18,7 +18,7 @@ [![GitHub stars](https://img.shields.io/github/stars/justlovemaki/AIClient-2-API.svg?style=flat&label=Star)](https://github.com/justlovemaki/AIClient-2-API/stargazers) [![GitHub issues](https://img.shields.io/github/issues/justlovemaki/AIClient-2-API.svg)](https://github.com/justlovemaki/AIClient-2-API/issues) -[中文](./README-ZH.md) | [English](./README.md) | [**👉 日本語**](./README-JA.md) | [**📚 完全ドキュメント**](https://aiproxy.justlikemaki.vip/ja/) +[**🔧 OpenClaw 設定**](./OPENCLAW_CONFIG_GUIDE-JA.md) | [中文](./README-ZH.md) | [English](./README.md) | [**👉 日本語**](./README-JA.md) | [**📚 完全ドキュメント**](https://aiproxy.justlikemaki.vip/ja/) diff --git a/README-ZH.md b/README-ZH.md index 2ef410d..c064c95 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -18,7 +18,7 @@ [![GitHub stars](https://img.shields.io/github/stars/justlovemaki/AIClient-2-API.svg?style=flat&label=Star)](https://github.com/justlovemaki/AIClient-2-API/stargazers) [![GitHub issues](https://img.shields.io/github/issues/justlovemaki/AIClient-2-API.svg)](https://github.com/justlovemaki/AIClient-2-API/issues) -[**👉 中文**](./README-ZH.md) | [English](./README.md) | [日本語](./README-JA.md) | [**📚 完整文档**](https://aiproxy.justlikemaki.vip/zh/) +[**🔧 OpenClaw 配置**](./OPENCLAW_CONFIG_GUIDE-ZH.md) | [**👉 中文**](./README-ZH.md) | [English](./README.md) | [日本語](./README-JA.md) | [**📚 完整文档**](https://aiproxy.justlikemaki.vip/zh/) diff --git a/README.md b/README.md index 52b77fb..3fa7f9b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ [![GitHub stars](https://img.shields.io/github/stars/justlovemaki/AIClient-2-API.svg?style=flat&label=Star)](https://github.com/justlovemaki/AIClient-2-API/stargazers) [![GitHub issues](https://img.shields.io/github/issues/justlovemaki/AIClient-2-API.svg)](https://github.com/justlovemaki/AIClient-2-API/issues) -[中文](./README-ZH.md) | [**👉 English**](./README.md) | [日本語](./README-JA.md) | [**📚 Documentation**](https://aiproxy.justlikemaki.vip/en/) +[**🔧 OpenClaw Config**](./OPENCLAW_CONFIG_GUIDE.md) | [中文](./README-ZH.md) | [**👉 English**](./README.md) | [日本語](./README-JA.md) | [**📚 Documentation**](https://aiproxy.justlikemaki.vip/en/) diff --git a/src/ui-modules/usage-api.js b/src/ui-modules/usage-api.js index e0e4a67..994d8ec 100644 --- a/src/ui-modules/usage-api.js +++ b/src/ui-modules/usage-api.js @@ -269,8 +269,14 @@ export async function handleGetUsage(req, res, currentConfig, providerPoolManage await writeUsageCache(usageResults); } + // Always include current server time + const finalResults = { + ...usageResults, + serverTime: new Date().toISOString() + }; + res.writeHead(200, { 'Content-Type': 'application/json' }); - res.end(JSON.stringify(usageResults)); + res.end(JSON.stringify(finalResults)); return true; } catch (error) { logger.error('[UI API] Failed to get usage:', error); @@ -300,7 +306,7 @@ export async function handleGetProviderUsage(req, res, currentConfig, providerPo const cachedData = await readProviderUsageCache(providerType); if (cachedData) { logger.info(`[Usage API] Returning cached usage data for ${providerType}`); - usageResults = cachedData; + usageResults = { ...cachedData, fromCache: true }; } } @@ -312,8 +318,14 @@ export async function handleGetProviderUsage(req, res, currentConfig, providerPo await updateProviderUsageCache(providerType, usageResults); } + // Always include current server time + const finalResults = { + ...usageResults, + serverTime: new Date().toISOString() + }; + res.writeHead(200, { 'Content-Type': 'application/json' }); - res.end(JSON.stringify(usageResults)); + res.end(JSON.stringify(finalResults)); return true; } catch (error) { logger.error(`[UI API] Failed to get usage for ${providerType}:`, error); diff --git a/static/app/i18n.js b/static/app/i18n.js index 92a848f..29c918b 100644 --- a/static/app/i18n.js +++ b/static/app/i18n.js @@ -515,6 +515,7 @@ const translations = { // Usage 'usage.title': '用量查询', 'usage.refresh': '刷新用量', + 'usage.serverTime': '服务端时间', 'usage.lastUpdate': '上次更新: {time}', 'usage.lastUpdateCache': '缓存时间: {time}', 'usage.supportedProvidersPrefix': '支持用量查询的提供商:', @@ -1285,6 +1286,7 @@ const translations = { // Usage 'usage.title': 'Usage Query', 'usage.refresh': 'Refresh Usage', + 'usage.serverTime': 'Server Time', 'usage.lastUpdate': 'Last Update: {time}', 'usage.lastUpdateCache': 'Cache Time: {time}', 'usage.supportedProvidersPrefix': 'Providers supporting usage query:', diff --git a/static/app/usage-manager.js b/static/app/usage-manager.js index 31ecfbc..62cb7b8 100644 --- a/static/app/usage-manager.js +++ b/static/app/usage-manager.js @@ -92,6 +92,14 @@ export async function loadUsage() { // 渲染用量数据 renderUsageData(data, contentEl); + // 更新服务端系统时间 + if (data.serverTime) { + const serverTimeEl = document.getElementById('serverTimeValue'); + if (serverTimeEl) { + serverTimeEl.textContent = new Date(data.serverTime).toLocaleString(getCurrentLanguage()); + } + } + // 更新最后更新时间 if (lastUpdateEl) { const timeStr = new Date(data.timestamp || Date.now()).toLocaleString(getCurrentLanguage()); @@ -155,6 +163,14 @@ export async function refreshUsage() { // 渲染用量数据 renderUsageData(data, contentEl); + // 更新服务端系统时间 + if (data.serverTime) { + const serverTimeEl = document.getElementById('serverTimeValue'); + if (serverTimeEl) { + serverTimeEl.textContent = new Date(data.serverTime).toLocaleString(getCurrentLanguage()); + } + } + // 更新最后更新时间 if (lastUpdateEl) { const timeStr = new Date().toLocaleString(getCurrentLanguage()); diff --git a/static/components/section-usage.css b/static/components/section-usage.css index 8d0f0f4..2e44ce9 100644 --- a/static/components/section-usage.css +++ b/static/components/section-usage.css @@ -18,6 +18,20 @@ .usage-last-update { font-size: 0.875rem; color: var(--text-secondary); + margin-left: auto; +} + +.server-time-display { + font-size: 0.875rem; + color: var(--text-secondary); + margin-left: 1.5rem; + padding-left: 1.5rem; + border-left: 1px solid var(--border-color); +} + +.server-time-display i { + color: var(--primary-color); + margin-right: 0.25rem; } .usage-info-banner { diff --git a/static/components/section-usage.html b/static/components/section-usage.html index 6dece64..63b7843 100644 --- a/static/components/section-usage.html +++ b/static/components/section-usage.html @@ -8,6 +8,9 @@ 刷新用量 上次更新: -- + + 服务端时间: -- +