From fb306242c24adc1bf4e9c823ad71451270bcfbf3 Mon Sep 17 00:00:00 2001 From: csdbit Date: Sat, 26 Jul 2025 01:53:23 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E7=BB=99API=20Keys=E5=92=8C=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E7=AE=A1=E7=90=86=E5=88=97=E8=A1=A8=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=85=B3=E9=94=AE=E5=AD=97=E6=AE=B5=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/admin/app.js | 118 +++++++++++++++++++++++++++++++++++++++++++ web/admin/index.html | 61 ++++++++++++++++++---- 2 files changed, 168 insertions(+), 11 deletions(-) diff --git a/web/admin/app.js b/web/admin/app.js index be8421cd..3390dd13 100644 --- a/web/admin/app.js +++ b/web/admin/app.js @@ -112,6 +112,8 @@ const app = createApp({ apiKeys: [], apiKeysLoading: false, apiKeyStatsTimeRange: 'all', // API Key统计时间范围:all, 7days, monthly + apiKeysSortBy: '', // 当前排序字段 + apiKeysSortOrder: 'asc', // 排序顺序 'asc' 或 'desc' showCreateApiKeyModal: false, createApiKeyLoading: false, apiKeyForm: { @@ -192,6 +194,8 @@ const app = createApp({ // 账户 accounts: [], accountsLoading: false, + accountsSortBy: '', // 当前排序字段 + accountsSortOrder: 'asc', // 排序顺序 'asc' 或 'desc' showCreateAccountModal: false, createAccountLoading: false, accountForm: { @@ -295,6 +299,83 @@ const app = createApp({ return `${window.location.protocol}//${window.location.host}/api/`; }, + // 排序后的账户列表 + sortedAccounts() { + if (!this.accountsSortBy) { + return this.accounts; + } + + return [...this.accounts].sort((a, b) => { + let aValue = a[this.accountsSortBy]; + let bValue = b[this.accountsSortBy]; + + // 特殊处理状态字段 + if (this.accountsSortBy === 'status') { + aValue = a.isActive ? 1 : 0; + bValue = b.isActive ? 1 : 0; + } + + // 处理字符串比较 + if (typeof aValue === 'string' && typeof bValue === 'string') { + aValue = aValue.toLowerCase(); + bValue = bValue.toLowerCase(); + } + + // 排序 + if (this.accountsSortOrder === 'asc') { + return aValue > bValue ? 1 : aValue < bValue ? -1 : 0; + } else { + return aValue < bValue ? 1 : aValue > bValue ? -1 : 0; + } + }); + }, + + // 排序后的API Keys列表 + sortedApiKeys() { + if (!this.apiKeysSortBy) { + return this.apiKeys; + } + + return [...this.apiKeys].sort((a, b) => { + let aValue, bValue; + + // 特殊处理不同字段 + switch (this.apiKeysSortBy) { + case 'status': + aValue = a.isActive ? 1 : 0; + bValue = b.isActive ? 1 : 0; + break; + case 'cost': + // 计算费用,转换为数字比较 + aValue = this.calculateApiKeyCostNumber(a.usage); + bValue = this.calculateApiKeyCostNumber(b.usage); + break; + case 'createdAt': + case 'expiresAt': + // 日期比较 + aValue = a[this.apiKeysSortBy] ? new Date(a[this.apiKeysSortBy]).getTime() : 0; + bValue = b[this.apiKeysSortBy] ? new Date(b[this.apiKeysSortBy]).getTime() : 0; + break; + default: + aValue = a[this.apiKeysSortBy]; + bValue = b[this.apiKeysSortBy]; + + // 处理字符串比较 + if (typeof aValue === 'string' && typeof bValue === 'string') { + aValue = aValue.toLowerCase(); + bValue = bValue.toLowerCase(); + } + } + + // 排序 + if (this.apiKeysSortOrder === 'asc') { + return aValue > bValue ? 1 : aValue < bValue ? -1 : 0; + } else { + return aValue < bValue ? 1 : aValue > bValue ? -1 : 0; + } + }); + }, + // 获取专属账号列表 dedicatedAccounts() { return this.accounts.filter(account => @@ -399,6 +480,30 @@ const app = createApp({ }, methods: { + // 账户列表排序 + sortAccounts(field) { + if (this.accountsSortBy === field) { + // 如果点击的是当前排序字段,切换排序顺序 + this.accountsSortOrder = this.accountsSortOrder === 'asc' ? 'desc' : 'asc'; + } else { + // 如果点击的是新字段,设置为升序 + this.accountsSortBy = field; + this.accountsSortOrder = 'asc'; + } + }, + + // API Keys列表排序 + sortApiKeys(field) { + if (this.apiKeysSortBy === field) { + // 如果点击的是当前排序字段,切换排序顺序 + this.apiKeysSortOrder = this.apiKeysSortOrder === 'asc' ? 'desc' : 'asc'; + } else { + // 如果点击的是新字段,设置为升序 + this.apiKeysSortBy = field; + this.apiKeysSortOrder = 'asc'; + } + }, + // 从URL读取tab参数并设置activeTab initializeTabFromUrl() { const urlParams = new URLSearchParams(window.location.search); @@ -3150,6 +3255,19 @@ const app = createApp({ // 如果没有后端费用数据,返回默认值 return '$0.000000'; }, + + // 计算API Key费用数值(用于排序) + calculateApiKeyCostNumber(usage) { + if (!usage || !usage.total) return 0; + + // 使用后端返回的准确费用数据 + if (usage.total.cost) { + return usage.total.cost; + } + + // 如果没有后端费用数据,返回0 + return 0; + }, // 初始化日期筛选器 initializeDateFilter() { diff --git a/web/admin/index.html b/web/admin/index.html index 8003b1b3..e5b9d7cf 100644 --- a/web/admin/index.html +++ b/web/admin/index.html @@ -575,17 +575,40 @@ - + - - - - + + + + -
名称 + 名称 + + + API Key状态使用统计创建时间过期时间 + 状态 + + + + 使用统计 + + (费用 + + ) + + + 创建时间 + + + + 过期时间 + + + 操作