mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 08:59:16 +00:00
Merge branch 'main' of https://github.com/Wei-Shaw/claude-relay-service
This commit is contained in:
150
web/admin/app.js
150
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: {
|
||||
@@ -199,6 +201,8 @@ const app = createApp({
|
||||
// 账户
|
||||
accounts: [],
|
||||
accountsLoading: false,
|
||||
accountSortBy: 'dailyTokens', // 默认按今日Token排序
|
||||
accountsSortOrder: 'asc', // 排序顺序 'asc' 或 'desc'
|
||||
showCreateAccountModal: false,
|
||||
createAccountLoading: false,
|
||||
accountForm: {
|
||||
@@ -302,6 +306,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 =>
|
||||
@@ -407,6 +488,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);
|
||||
@@ -1888,6 +1993,9 @@ const app = createApp({
|
||||
account.boundApiKeysCount = this.apiKeys.filter(key => key.geminiAccountId === account.id).length;
|
||||
}
|
||||
});
|
||||
|
||||
// 加载完成后自动排序
|
||||
this.sortAccounts();
|
||||
} catch (error) {
|
||||
console.error('Failed to load accounts:', error);
|
||||
} finally {
|
||||
@@ -1895,6 +2003,35 @@ const app = createApp({
|
||||
}
|
||||
},
|
||||
|
||||
// 账户排序
|
||||
sortAccounts() {
|
||||
if (!this.accounts || this.accounts.length === 0) return;
|
||||
|
||||
this.accounts.sort((a, b) => {
|
||||
switch (this.accountSortBy) {
|
||||
case 'name':
|
||||
return a.name.localeCompare(b.name);
|
||||
case 'dailyTokens':
|
||||
const aTokens = (a.usage && a.usage.daily && a.usage.daily.allTokens) || 0;
|
||||
const bTokens = (b.usage && b.usage.daily && b.usage.daily.allTokens) || 0;
|
||||
return bTokens - aTokens; // 降序
|
||||
case 'dailyRequests':
|
||||
const aRequests = (a.usage && a.usage.daily && a.usage.daily.requests) || 0;
|
||||
const bRequests = (b.usage && b.usage.daily && b.usage.daily.requests) || 0;
|
||||
return bRequests - aRequests; // 降序
|
||||
case 'totalTokens':
|
||||
const aTotalTokens = (a.usage && a.usage.total && a.usage.total.allTokens) || 0;
|
||||
const bTotalTokens = (b.usage && b.usage.total && b.usage.total.allTokens) || 0;
|
||||
return bTotalTokens - aTotalTokens; // 降序
|
||||
case 'lastUsed':
|
||||
const aLastUsed = a.lastUsedAt ? new Date(a.lastUsedAt) : new Date(0);
|
||||
const bLastUsed = b.lastUsedAt ? new Date(b.lastUsedAt) : new Date(0);
|
||||
return bLastUsed - aLastUsed; // 降序(最近使用的在前)
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async loadModelStats() {
|
||||
this.modelStatsLoading = true;
|
||||
@@ -3180,6 +3317,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() {
|
||||
|
||||
Reference in New Issue
Block a user