2025-12-31 23:57:01 +08:00
|
|
|
|
<template>
|
2026-01-04 10:22:02 +08:00
|
|
|
|
<div v-if="shouldShowQuota">
|
|
|
|
|
|
<!-- First line: Platform + Tier Badge -->
|
|
|
|
|
|
<div class="mb-1 flex items-center gap-1">
|
|
|
|
|
|
<span :class="['badge text-xs px-2 py-0.5 rounded font-medium', tierBadgeClass]">
|
|
|
|
|
|
{{ tierLabel }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
2025-12-31 23:57:01 +08:00
|
|
|
|
|
2026-01-04 10:22:02 +08:00
|
|
|
|
<!-- Usage status: unlimited flow or rate limit -->
|
|
|
|
|
|
<div class="text-xs text-gray-400 dark:text-gray-500">
|
|
|
|
|
|
<span v-if="!isRateLimited">
|
|
|
|
|
|
{{ t('admin.accounts.gemini.rateLimit.unlimited') }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span
|
|
|
|
|
|
v-else
|
|
|
|
|
|
:class="[
|
|
|
|
|
|
'font-medium',
|
|
|
|
|
|
isUrgent
|
|
|
|
|
|
? 'text-red-600 dark:text-red-400 animate-pulse'
|
|
|
|
|
|
: 'text-amber-600 dark:text-amber-400'
|
|
|
|
|
|
]"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ t('admin.accounts.gemini.rateLimit.limited', { time: resetCountdown }) }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
2025-12-31 23:57:01 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { computed, ref, watch, onUnmounted } from 'vue'
|
2026-01-01 08:29:57 +08:00
|
|
|
|
import { useI18n } from 'vue-i18n'
|
2025-12-31 23:57:01 +08:00
|
|
|
|
import type { Account, GeminiCredentials } from '@/types'
|
|
|
|
|
|
|
|
|
|
|
|
const props = defineProps<{
|
|
|
|
|
|
account: Account
|
|
|
|
|
|
}>()
|
|
|
|
|
|
|
2026-01-01 08:29:57 +08:00
|
|
|
|
const { t } = useI18n()
|
|
|
|
|
|
|
2025-12-31 23:57:01 +08:00
|
|
|
|
const now = ref(new Date())
|
|
|
|
|
|
let timer: ReturnType<typeof setInterval> | null = null
|
|
|
|
|
|
|
|
|
|
|
|
// 是否为 Code Assist OAuth
|
|
|
|
|
|
// 判断逻辑与后端保持一致:project_id 存在即为 Code Assist
|
|
|
|
|
|
const isCodeAssist = computed(() => {
|
|
|
|
|
|
const creds = props.account.credentials as GeminiCredentials | undefined
|
|
|
|
|
|
// 显式为 code_assist,或 legacy 情况(oauth_type 为空但 project_id 存在)
|
|
|
|
|
|
return creds?.oauth_type === 'code_assist' || (!creds?.oauth_type && !!creds?.project_id)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
feat(gemini): 添加 Google One 存储空间推断 Tier 功能
## 功能概述
通过 Google Drive API 获取存储空间配额来推断 Google One 订阅等级,并优化统一的配额显示系统。
## 后端改动
- 新增 Drive API 客户端 (drive_client.go)
- 支持代理和指数退避重试
- 处理 403/429 错误
- 添加 Tier 推断逻辑 (inferGoogleOneTier)
- 支持 6 种 tier 类型:AI_PREMIUM, GOOGLE_ONE_STANDARD, GOOGLE_ONE_BASIC, FREE, GOOGLE_ONE_UNKNOWN, GOOGLE_ONE_UNLIMITED
- 集成到 OAuth 流程
- ExchangeCode: 授权时自动获取 tier
- RefreshAccountToken: Token 刷新时更新 tier (24小时缓存)
- 新增管理 API 端点
- POST /api/v1/admin/accounts/:id/refresh-tier (单个账号刷新)
- POST /api/v1/admin/accounts/batch-refresh-tier (批量刷新)
## 前端改动
- 更新 AccountQuotaInfo.vue
- 添加 Google One tier 标签映射
- 添加 tier 颜色样式 (紫色/蓝色/绿色/灰色/琥珀色)
- 更新 AccountUsageCell.vue
- 添加 Google One tier 显示逻辑
- 根据 oauth_type 区分显示方式
- 添加国际化翻译 (en.ts, zh.ts)
- aiPremium, standard, basic, free, personal, unlimited
## Tier 推断规则
- >= 2TB: AI Premium
- >= 200GB: Google One Standard
- >= 100GB: Google One Basic
- >= 15GB: Free
- > 100TB: Unlimited (G Suite legacy)
- 其他/失败: Unknown (显示为 Personal)
## 优雅降级
- Drive API 失败时使用 GOOGLE_ONE_UNKNOWN
- 不阻断 OAuth 流程
- 24小时缓存避免频繁调用
## 测试
- ✅ 后端编译成功
- ✅ 前端构建成功
- ✅ 所有代码符合现有规范
2025-12-31 21:45:24 -08:00
|
|
|
|
// 是否为 Google One OAuth
|
|
|
|
|
|
const isGoogleOne = computed(() => {
|
|
|
|
|
|
const creds = props.account.credentials as GeminiCredentials | undefined
|
|
|
|
|
|
return creds?.oauth_type === 'google_one'
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2025-12-31 23:57:01 +08:00
|
|
|
|
// 是否应该显示配额信息
|
|
|
|
|
|
const shouldShowQuota = computed(() => {
|
|
|
|
|
|
return props.account.platform === 'gemini'
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// Tier 标签文本
|
|
|
|
|
|
const tierLabel = computed(() => {
|
feat(gemini): 添加 Google One 存储空间推断 Tier 功能
## 功能概述
通过 Google Drive API 获取存储空间配额来推断 Google One 订阅等级,并优化统一的配额显示系统。
## 后端改动
- 新增 Drive API 客户端 (drive_client.go)
- 支持代理和指数退避重试
- 处理 403/429 错误
- 添加 Tier 推断逻辑 (inferGoogleOneTier)
- 支持 6 种 tier 类型:AI_PREMIUM, GOOGLE_ONE_STANDARD, GOOGLE_ONE_BASIC, FREE, GOOGLE_ONE_UNKNOWN, GOOGLE_ONE_UNLIMITED
- 集成到 OAuth 流程
- ExchangeCode: 授权时自动获取 tier
- RefreshAccountToken: Token 刷新时更新 tier (24小时缓存)
- 新增管理 API 端点
- POST /api/v1/admin/accounts/:id/refresh-tier (单个账号刷新)
- POST /api/v1/admin/accounts/batch-refresh-tier (批量刷新)
## 前端改动
- 更新 AccountQuotaInfo.vue
- 添加 Google One tier 标签映射
- 添加 tier 颜色样式 (紫色/蓝色/绿色/灰色/琥珀色)
- 更新 AccountUsageCell.vue
- 添加 Google One tier 显示逻辑
- 根据 oauth_type 区分显示方式
- 添加国际化翻译 (en.ts, zh.ts)
- aiPremium, standard, basic, free, personal, unlimited
## Tier 推断规则
- >= 2TB: AI Premium
- >= 200GB: Google One Standard
- >= 100GB: Google One Basic
- >= 15GB: Free
- > 100TB: Unlimited (G Suite legacy)
- 其他/失败: Unknown (显示为 Personal)
## 优雅降级
- Drive API 失败时使用 GOOGLE_ONE_UNKNOWN
- 不阻断 OAuth 流程
- 24小时缓存避免频繁调用
## 测试
- ✅ 后端编译成功
- ✅ 前端构建成功
- ✅ 所有代码符合现有规范
2025-12-31 21:45:24 -08:00
|
|
|
|
const creds = props.account.credentials as GeminiCredentials | undefined
|
|
|
|
|
|
|
2025-12-31 23:57:01 +08:00
|
|
|
|
if (isCodeAssist.value) {
|
2026-01-04 15:36:00 +08:00
|
|
|
|
const tier = (creds?.tier_id || '').toString().trim().toLowerCase()
|
|
|
|
|
|
if (tier === 'gcp_enterprise') return 'GCP Enterprise'
|
|
|
|
|
|
if (tier === 'gcp_standard') return 'GCP Standard'
|
|
|
|
|
|
// Backward compatibility
|
|
|
|
|
|
const upper = (creds?.tier_id || '').toString().trim().toUpperCase()
|
|
|
|
|
|
if (upper.includes('ULTRA') || upper.includes('ENTERPRISE')) return 'GCP Enterprise'
|
|
|
|
|
|
if (upper) return `GCP ${upper}`
|
|
|
|
|
|
return 'GCP'
|
feat(gemini): 添加 Google One 存储空间推断 Tier 功能
## 功能概述
通过 Google Drive API 获取存储空间配额来推断 Google One 订阅等级,并优化统一的配额显示系统。
## 后端改动
- 新增 Drive API 客户端 (drive_client.go)
- 支持代理和指数退避重试
- 处理 403/429 错误
- 添加 Tier 推断逻辑 (inferGoogleOneTier)
- 支持 6 种 tier 类型:AI_PREMIUM, GOOGLE_ONE_STANDARD, GOOGLE_ONE_BASIC, FREE, GOOGLE_ONE_UNKNOWN, GOOGLE_ONE_UNLIMITED
- 集成到 OAuth 流程
- ExchangeCode: 授权时自动获取 tier
- RefreshAccountToken: Token 刷新时更新 tier (24小时缓存)
- 新增管理 API 端点
- POST /api/v1/admin/accounts/:id/refresh-tier (单个账号刷新)
- POST /api/v1/admin/accounts/batch-refresh-tier (批量刷新)
## 前端改动
- 更新 AccountQuotaInfo.vue
- 添加 Google One tier 标签映射
- 添加 tier 颜色样式 (紫色/蓝色/绿色/灰色/琥珀色)
- 更新 AccountUsageCell.vue
- 添加 Google One tier 显示逻辑
- 根据 oauth_type 区分显示方式
- 添加国际化翻译 (en.ts, zh.ts)
- aiPremium, standard, basic, free, personal, unlimited
## Tier 推断规则
- >= 2TB: AI Premium
- >= 200GB: Google One Standard
- >= 100GB: Google One Basic
- >= 15GB: Free
- > 100TB: Unlimited (G Suite legacy)
- 其他/失败: Unknown (显示为 Personal)
## 优雅降级
- Drive API 失败时使用 GOOGLE_ONE_UNKNOWN
- 不阻断 OAuth 流程
- 24小时缓存避免频繁调用
## 测试
- ✅ 后端编译成功
- ✅ 前端构建成功
- ✅ 所有代码符合现有规范
2025-12-31 21:45:24 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (isGoogleOne.value) {
|
2026-01-04 15:36:00 +08:00
|
|
|
|
const tier = (creds?.tier_id || '').toString().trim().toLowerCase()
|
|
|
|
|
|
if (tier === 'google_ai_ultra') return 'Google AI Ultra'
|
|
|
|
|
|
if (tier === 'google_ai_pro') return 'Google AI Pro'
|
|
|
|
|
|
if (tier === 'google_one_free') return 'Google One Free'
|
|
|
|
|
|
// Backward compatibility
|
|
|
|
|
|
const upper = (creds?.tier_id || '').toString().trim().toUpperCase()
|
|
|
|
|
|
if (upper === 'AI_PREMIUM') return 'Google AI Pro'
|
|
|
|
|
|
if (upper === 'GOOGLE_ONE_UNLIMITED') return 'Google AI Ultra'
|
|
|
|
|
|
if (upper) return `Google One ${upper}`
|
|
|
|
|
|
return 'Google One'
|
2025-12-31 23:57:01 +08:00
|
|
|
|
}
|
feat(gemini): 添加 Google One 存储空间推断 Tier 功能
## 功能概述
通过 Google Drive API 获取存储空间配额来推断 Google One 订阅等级,并优化统一的配额显示系统。
## 后端改动
- 新增 Drive API 客户端 (drive_client.go)
- 支持代理和指数退避重试
- 处理 403/429 错误
- 添加 Tier 推断逻辑 (inferGoogleOneTier)
- 支持 6 种 tier 类型:AI_PREMIUM, GOOGLE_ONE_STANDARD, GOOGLE_ONE_BASIC, FREE, GOOGLE_ONE_UNKNOWN, GOOGLE_ONE_UNLIMITED
- 集成到 OAuth 流程
- ExchangeCode: 授权时自动获取 tier
- RefreshAccountToken: Token 刷新时更新 tier (24小时缓存)
- 新增管理 API 端点
- POST /api/v1/admin/accounts/:id/refresh-tier (单个账号刷新)
- POST /api/v1/admin/accounts/batch-refresh-tier (批量刷新)
## 前端改动
- 更新 AccountQuotaInfo.vue
- 添加 Google One tier 标签映射
- 添加 tier 颜色样式 (紫色/蓝色/绿色/灰色/琥珀色)
- 更新 AccountUsageCell.vue
- 添加 Google One tier 显示逻辑
- 根据 oauth_type 区分显示方式
- 添加国际化翻译 (en.ts, zh.ts)
- aiPremium, standard, basic, free, personal, unlimited
## Tier 推断规则
- >= 2TB: AI Premium
- >= 200GB: Google One Standard
- >= 100GB: Google One Basic
- >= 15GB: Free
- > 100TB: Unlimited (G Suite legacy)
- 其他/失败: Unknown (显示为 Personal)
## 优雅降级
- Drive API 失败时使用 GOOGLE_ONE_UNKNOWN
- 不阻断 OAuth 流程
- 24小时缓存避免频繁调用
## 测试
- ✅ 后端编译成功
- ✅ 前端构建成功
- ✅ 所有代码符合现有规范
2025-12-31 21:45:24 -08:00
|
|
|
|
|
2026-01-04 10:39:28 +08:00
|
|
|
|
// API Key: 显示 AI Studio
|
2026-01-04 15:36:00 +08:00
|
|
|
|
const tier = (creds?.tier_id || '').toString().trim().toLowerCase()
|
|
|
|
|
|
if (tier === 'aistudio_paid') return 'AI Studio Pay-as-you-go'
|
|
|
|
|
|
if (tier === 'aistudio_free') return 'AI Studio Free Tier'
|
2026-01-04 10:39:28 +08:00
|
|
|
|
return 'AI Studio'
|
2025-12-31 23:57:01 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
2026-01-04 10:42:37 +08:00
|
|
|
|
// Tier Badge 样式(统一样式)
|
2025-12-31 23:57:01 +08:00
|
|
|
|
const tierBadgeClass = computed(() => {
|
|
|
|
|
|
const creds = props.account.credentials as GeminiCredentials | undefined
|
feat(gemini): 添加 Google One 存储空间推断 Tier 功能
## 功能概述
通过 Google Drive API 获取存储空间配额来推断 Google One 订阅等级,并优化统一的配额显示系统。
## 后端改动
- 新增 Drive API 客户端 (drive_client.go)
- 支持代理和指数退避重试
- 处理 403/429 错误
- 添加 Tier 推断逻辑 (inferGoogleOneTier)
- 支持 6 种 tier 类型:AI_PREMIUM, GOOGLE_ONE_STANDARD, GOOGLE_ONE_BASIC, FREE, GOOGLE_ONE_UNKNOWN, GOOGLE_ONE_UNLIMITED
- 集成到 OAuth 流程
- ExchangeCode: 授权时自动获取 tier
- RefreshAccountToken: Token 刷新时更新 tier (24小时缓存)
- 新增管理 API 端点
- POST /api/v1/admin/accounts/:id/refresh-tier (单个账号刷新)
- POST /api/v1/admin/accounts/batch-refresh-tier (批量刷新)
## 前端改动
- 更新 AccountQuotaInfo.vue
- 添加 Google One tier 标签映射
- 添加 tier 颜色样式 (紫色/蓝色/绿色/灰色/琥珀色)
- 更新 AccountUsageCell.vue
- 添加 Google One tier 显示逻辑
- 根据 oauth_type 区分显示方式
- 添加国际化翻译 (en.ts, zh.ts)
- aiPremium, standard, basic, free, personal, unlimited
## Tier 推断规则
- >= 2TB: AI Premium
- >= 200GB: Google One Standard
- >= 100GB: Google One Basic
- >= 15GB: Free
- > 100TB: Unlimited (G Suite legacy)
- 其他/失败: Unknown (显示为 Personal)
## 优雅降级
- Drive API 失败时使用 GOOGLE_ONE_UNKNOWN
- 不阻断 OAuth 流程
- 24小时缓存避免频繁调用
## 测试
- ✅ 后端编译成功
- ✅ 前端构建成功
- ✅ 所有代码符合现有规范
2025-12-31 21:45:24 -08:00
|
|
|
|
|
|
|
|
|
|
if (isCodeAssist.value) {
|
2026-01-04 15:36:00 +08:00
|
|
|
|
const tier = (creds?.tier_id || '').toString().trim().toLowerCase()
|
|
|
|
|
|
if (tier === 'gcp_enterprise') return 'bg-purple-100 text-purple-600 dark:bg-purple-900/40 dark:text-purple-300'
|
|
|
|
|
|
if (tier === 'gcp_standard') return 'bg-blue-100 text-blue-600 dark:bg-blue-900/40 dark:text-blue-300'
|
|
|
|
|
|
// Backward compatibility
|
|
|
|
|
|
const upper = (creds?.tier_id || '').toString().trim().toUpperCase()
|
|
|
|
|
|
if (upper.includes('ULTRA') || upper.includes('ENTERPRISE')) return 'bg-purple-100 text-purple-600 dark:bg-purple-900/40 dark:text-purple-300'
|
|
|
|
|
|
return 'bg-blue-100 text-blue-600 dark:bg-blue-900/40 dark:text-blue-300'
|
2025-12-31 23:57:01 +08:00
|
|
|
|
}
|
feat(gemini): 添加 Google One 存储空间推断 Tier 功能
## 功能概述
通过 Google Drive API 获取存储空间配额来推断 Google One 订阅等级,并优化统一的配额显示系统。
## 后端改动
- 新增 Drive API 客户端 (drive_client.go)
- 支持代理和指数退避重试
- 处理 403/429 错误
- 添加 Tier 推断逻辑 (inferGoogleOneTier)
- 支持 6 种 tier 类型:AI_PREMIUM, GOOGLE_ONE_STANDARD, GOOGLE_ONE_BASIC, FREE, GOOGLE_ONE_UNKNOWN, GOOGLE_ONE_UNLIMITED
- 集成到 OAuth 流程
- ExchangeCode: 授权时自动获取 tier
- RefreshAccountToken: Token 刷新时更新 tier (24小时缓存)
- 新增管理 API 端点
- POST /api/v1/admin/accounts/:id/refresh-tier (单个账号刷新)
- POST /api/v1/admin/accounts/batch-refresh-tier (批量刷新)
## 前端改动
- 更新 AccountQuotaInfo.vue
- 添加 Google One tier 标签映射
- 添加 tier 颜色样式 (紫色/蓝色/绿色/灰色/琥珀色)
- 更新 AccountUsageCell.vue
- 添加 Google One tier 显示逻辑
- 根据 oauth_type 区分显示方式
- 添加国际化翻译 (en.ts, zh.ts)
- aiPremium, standard, basic, free, personal, unlimited
## Tier 推断规则
- >= 2TB: AI Premium
- >= 200GB: Google One Standard
- >= 100GB: Google One Basic
- >= 15GB: Free
- > 100TB: Unlimited (G Suite legacy)
- 其他/失败: Unknown (显示为 Personal)
## 优雅降级
- Drive API 失败时使用 GOOGLE_ONE_UNKNOWN
- 不阻断 OAuth 流程
- 24小时缓存避免频繁调用
## 测试
- ✅ 后端编译成功
- ✅ 前端构建成功
- ✅ 所有代码符合现有规范
2025-12-31 21:45:24 -08:00
|
|
|
|
|
|
|
|
|
|
if (isGoogleOne.value) {
|
2026-01-04 15:36:00 +08:00
|
|
|
|
const tier = (creds?.tier_id || '').toString().trim().toLowerCase()
|
|
|
|
|
|
if (tier === 'google_ai_ultra') return 'bg-purple-100 text-purple-600 dark:bg-purple-900/40 dark:text-purple-300'
|
|
|
|
|
|
if (tier === 'google_ai_pro') return 'bg-blue-100 text-blue-600 dark:bg-blue-900/40 dark:text-blue-300'
|
|
|
|
|
|
if (tier === 'google_one_free') return 'bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300'
|
|
|
|
|
|
// Backward compatibility
|
|
|
|
|
|
const upper = (creds?.tier_id || '').toString().trim().toUpperCase()
|
|
|
|
|
|
if (upper === 'GOOGLE_ONE_UNLIMITED') return 'bg-purple-100 text-purple-600 dark:bg-purple-900/40 dark:text-purple-300'
|
|
|
|
|
|
if (upper === 'AI_PREMIUM') return 'bg-blue-100 text-blue-600 dark:bg-blue-900/40 dark:text-blue-300'
|
|
|
|
|
|
return 'bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300'
|
feat(gemini): 添加 Google One 存储空间推断 Tier 功能
## 功能概述
通过 Google Drive API 获取存储空间配额来推断 Google One 订阅等级,并优化统一的配额显示系统。
## 后端改动
- 新增 Drive API 客户端 (drive_client.go)
- 支持代理和指数退避重试
- 处理 403/429 错误
- 添加 Tier 推断逻辑 (inferGoogleOneTier)
- 支持 6 种 tier 类型:AI_PREMIUM, GOOGLE_ONE_STANDARD, GOOGLE_ONE_BASIC, FREE, GOOGLE_ONE_UNKNOWN, GOOGLE_ONE_UNLIMITED
- 集成到 OAuth 流程
- ExchangeCode: 授权时自动获取 tier
- RefreshAccountToken: Token 刷新时更新 tier (24小时缓存)
- 新增管理 API 端点
- POST /api/v1/admin/accounts/:id/refresh-tier (单个账号刷新)
- POST /api/v1/admin/accounts/batch-refresh-tier (批量刷新)
## 前端改动
- 更新 AccountQuotaInfo.vue
- 添加 Google One tier 标签映射
- 添加 tier 颜色样式 (紫色/蓝色/绿色/灰色/琥珀色)
- 更新 AccountUsageCell.vue
- 添加 Google One tier 显示逻辑
- 根据 oauth_type 区分显示方式
- 添加国际化翻译 (en.ts, zh.ts)
- aiPremium, standard, basic, free, personal, unlimited
## Tier 推断规则
- >= 2TB: AI Premium
- >= 200GB: Google One Standard
- >= 100GB: Google One Basic
- >= 15GB: Free
- > 100TB: Unlimited (G Suite legacy)
- 其他/失败: Unknown (显示为 Personal)
## 优雅降级
- Drive API 失败时使用 GOOGLE_ONE_UNKNOWN
- 不阻断 OAuth 流程
- 24小时缓存避免频繁调用
## 测试
- ✅ 后端编译成功
- ✅ 前端构建成功
- ✅ 所有代码符合现有规范
2025-12-31 21:45:24 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// AI Studio 默认样式:蓝色
|
2026-01-04 15:36:00 +08:00
|
|
|
|
const tier = (creds?.tier_id || '').toString().trim().toLowerCase()
|
|
|
|
|
|
if (tier === 'aistudio_paid') return 'bg-blue-100 text-blue-600 dark:bg-blue-900/40 dark:text-blue-300'
|
|
|
|
|
|
if (tier === 'aistudio_free') return 'bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300'
|
2026-01-04 10:42:37 +08:00
|
|
|
|
return 'bg-blue-100 text-blue-600 dark:bg-blue-900/40 dark:text-blue-300'
|
2025-12-31 23:57:01 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 是否限流
|
|
|
|
|
|
const isRateLimited = computed(() => {
|
|
|
|
|
|
if (!props.account.rate_limit_reset_at) return false
|
|
|
|
|
|
const resetTime = Date.parse(props.account.rate_limit_reset_at)
|
|
|
|
|
|
// 防护:如果日期解析失败(NaN),则认为未限流
|
|
|
|
|
|
if (Number.isNaN(resetTime)) return false
|
|
|
|
|
|
return resetTime > now.value.getTime()
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 倒计时文本
|
|
|
|
|
|
const resetCountdown = computed(() => {
|
|
|
|
|
|
if (!props.account.rate_limit_reset_at) return ''
|
|
|
|
|
|
const resetTime = Date.parse(props.account.rate_limit_reset_at)
|
|
|
|
|
|
// 防护:如果日期解析失败,显示 "-"
|
|
|
|
|
|
if (Number.isNaN(resetTime)) return '-'
|
|
|
|
|
|
|
|
|
|
|
|
const diffMs = resetTime - now.value.getTime()
|
2026-01-01 08:29:57 +08:00
|
|
|
|
if (diffMs <= 0) return t('admin.accounts.gemini.rateLimit.now')
|
2025-12-31 23:57:01 +08:00
|
|
|
|
|
|
|
|
|
|
const diffSeconds = Math.floor(diffMs / 1000)
|
|
|
|
|
|
const diffMinutes = Math.floor(diffSeconds / 60)
|
|
|
|
|
|
const diffHours = Math.floor(diffMinutes / 60)
|
|
|
|
|
|
|
|
|
|
|
|
if (diffMinutes < 1) return `${diffSeconds}s`
|
|
|
|
|
|
if (diffHours < 1) {
|
|
|
|
|
|
const secs = diffSeconds % 60
|
|
|
|
|
|
return `${diffMinutes}m ${secs}s`
|
|
|
|
|
|
}
|
|
|
|
|
|
const mins = diffMinutes % 60
|
|
|
|
|
|
return `${diffHours}h ${mins}m`
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 是否紧急(< 1分钟)
|
|
|
|
|
|
const isUrgent = computed(() => {
|
|
|
|
|
|
if (!props.account.rate_limit_reset_at) return false
|
|
|
|
|
|
const resetTime = Date.parse(props.account.rate_limit_reset_at)
|
|
|
|
|
|
// 防护:如果日期解析失败,返回 false
|
|
|
|
|
|
if (Number.isNaN(resetTime)) return false
|
|
|
|
|
|
|
|
|
|
|
|
const diffMs = resetTime - now.value.getTime()
|
|
|
|
|
|
return diffMs > 0 && diffMs < 60000
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 监听限流状态,动态启动/停止定时器
|
|
|
|
|
|
watch(
|
|
|
|
|
|
() => isRateLimited.value,
|
|
|
|
|
|
(limited) => {
|
|
|
|
|
|
if (limited && !timer) {
|
|
|
|
|
|
// 进入限流状态,启动定时器
|
|
|
|
|
|
timer = setInterval(() => {
|
|
|
|
|
|
now.value = new Date()
|
|
|
|
|
|
}, 1000)
|
|
|
|
|
|
} else if (!limited && timer) {
|
|
|
|
|
|
// 解除限流,停止定时器
|
|
|
|
|
|
clearInterval(timer)
|
|
|
|
|
|
timer = null
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
{ immediate: true } // 立即执行,确保挂载时已限流的情况也能启动定时器
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
|
if (timer !== null) {
|
|
|
|
|
|
clearInterval(timer)
|
|
|
|
|
|
timer = null
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
</script>
|