feat: 改进管理界面弹窗体验和滚动条美化

- 修复API Key创建/编辑弹窗和账户信息修改弹窗在低高度屏幕上被遮挡的问题
- 为所有弹窗添加自适应高度支持,最大高度限制为90vh
- 美化Claude账户弹窗的滚动条样式,使用紫蓝渐变色与主题保持一致
- 添加响应式适配,移动设备上弹窗高度调整为85vh
- 优化滚动条交互体验,支持悬停和激活状态

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
shaw
2025-07-18 23:49:55 +08:00
parent 6988be0806
commit f5968e518e
9 changed files with 1148 additions and 96 deletions

View File

@@ -139,18 +139,24 @@ class RedisClient {
// 📊 使用统计相关操作支持缓存token统计和模型信息
async incrementTokenUsage(keyId, tokens, inputTokens = 0, outputTokens = 0, cacheCreateTokens = 0, cacheReadTokens = 0, model = 'unknown') {
const key = `usage:${keyId}`;
const today = new Date().toISOString().split('T')[0];
const currentMonth = `${new Date().getFullYear()}-${String(new Date().getMonth() + 1).padStart(2, '0')}`;
const now = new Date();
const today = now.toISOString().split('T')[0];
const currentMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
const currentHour = `${today}:${String(now.getHours()).padStart(2, '0')}`; // 新增小时级别
const daily = `usage:daily:${keyId}:${today}`;
const monthly = `usage:monthly:${keyId}:${currentMonth}`;
const hourly = `usage:hourly:${keyId}:${currentHour}`; // 新增小时级别key
// 按模型统计的键
const modelDaily = `usage:model:daily:${model}:${today}`;
const modelMonthly = `usage:model:monthly:${model}:${currentMonth}`;
const modelHourly = `usage:model:hourly:${model}:${currentHour}`; // 新增模型小时级别
// API Key级别的模型统计
const keyModelDaily = `usage:${keyId}:model:daily:${model}:${today}`;
const keyModelMonthly = `usage:${keyId}:model:monthly:${model}:${currentMonth}`;
const keyModelHourly = `usage:${keyId}:model:hourly:${model}:${currentHour}`; // 新增API Key模型小时级别
// 智能处理输入输出token分配
const finalInputTokens = inputTokens || 0;
@@ -218,13 +224,40 @@ class RedisClient {
this.client.hincrby(keyModelMonthly, 'cacheReadTokens', finalCacheReadTokens),
this.client.hincrby(keyModelMonthly, 'allTokens', totalTokens),
this.client.hincrby(keyModelMonthly, 'requests', 1),
// 小时级别统计
this.client.hincrby(hourly, 'tokens', coreTokens),
this.client.hincrby(hourly, 'inputTokens', finalInputTokens),
this.client.hincrby(hourly, 'outputTokens', finalOutputTokens),
this.client.hincrby(hourly, 'cacheCreateTokens', finalCacheCreateTokens),
this.client.hincrby(hourly, 'cacheReadTokens', finalCacheReadTokens),
this.client.hincrby(hourly, 'allTokens', totalTokens),
this.client.hincrby(hourly, 'requests', 1),
// 按模型统计 - 每小时
this.client.hincrby(modelHourly, 'inputTokens', finalInputTokens),
this.client.hincrby(modelHourly, 'outputTokens', finalOutputTokens),
this.client.hincrby(modelHourly, 'cacheCreateTokens', finalCacheCreateTokens),
this.client.hincrby(modelHourly, 'cacheReadTokens', finalCacheReadTokens),
this.client.hincrby(modelHourly, 'allTokens', totalTokens),
this.client.hincrby(modelHourly, 'requests', 1),
// API Key级别的模型统计 - 每小时
this.client.hincrby(keyModelHourly, 'inputTokens', finalInputTokens),
this.client.hincrby(keyModelHourly, 'outputTokens', finalOutputTokens),
this.client.hincrby(keyModelHourly, 'cacheCreateTokens', finalCacheCreateTokens),
this.client.hincrby(keyModelHourly, 'cacheReadTokens', finalCacheReadTokens),
this.client.hincrby(keyModelHourly, 'allTokens', totalTokens),
this.client.hincrby(keyModelHourly, 'requests', 1),
// 设置过期时间
this.client.expire(daily, 86400 * 32), // 32天过期
this.client.expire(monthly, 86400 * 365), // 1年过期
this.client.expire(hourly, 86400 * 7), // 小时统计7天过期
this.client.expire(modelDaily, 86400 * 32), // 模型每日统计32天过期
this.client.expire(modelMonthly, 86400 * 365), // 模型每月统计1年过期
this.client.expire(modelHourly, 86400 * 7), // 模型小时统计7天过期
this.client.expire(keyModelDaily, 86400 * 32), // API Key模型每日统计32天过期
this.client.expire(keyModelMonthly, 86400 * 365) // API Key模型每月统计1年过期
this.client.expire(keyModelMonthly, 86400 * 365), // API Key模型每月统计1年过期
this.client.expire(keyModelHourly, 86400 * 7) // API Key模型小时统计7天过期
]);
}