import { ArrowRightLeft, RefreshCw, Trash2, Download, Info, Lock, Ban, Diamond, Gem, Circle, Clock, ToggleLeft, ToggleRight, Fingerprint } from 'lucide-react'; import { Account } from '../../types/account'; import { getQuotaColor, formatTimeRemaining, getTimeRemainingColor } from '../../utils/format'; import { cn } from '../../utils/cn'; import { useTranslation } from 'react-i18next'; interface AccountRowProps { account: Account; selected: boolean; onSelect: () => void; isCurrent: boolean; isRefreshing: boolean; isSwitching?: boolean; onSwitch: () => void; onRefresh: () => void; onViewDevice: () => void; onViewDetails: () => void; onExport: () => void; onDelete: () => void; onToggleProxy: () => void; } function AccountRow({ account, selected, onSelect, isCurrent, isRefreshing, isSwitching = false, onSwitch, onRefresh, onViewDetails, onExport, onDelete, onToggleProxy, onViewDevice }: AccountRowProps) { const { t } = useTranslation(); // [重构] 按组聚合查找逻辑,优先显示组内配额最低的型号以与锁定状态(🔒)对齐 const geminiProModel = account.quota?.models .filter(m => m.name.toLowerCase() === 'gemini-3-pro-high' || m.name.toLowerCase() === 'gemini-3-pro-low' || m.name.toLowerCase() === 'gemini-3.1-pro-high' || m.name.toLowerCase() === 'gemini-3.1-pro-low' ) .sort((a, b) => (a.percentage || 0) - (b.percentage || 0))[0]; const geminiFlashModel = account.quota?.models.find(m => m.name.toLowerCase() === 'gemini-3-flash'); const geminiImageModel = account.quota?.models.find(m => m.name.toLowerCase() === 'gemini-3-pro-image'); const claudeGroupNames = [ 'claude-opus-4-6-thinking', 'claude' ]; const claudeModel = account.quota?.models .filter(m => claudeGroupNames.includes(m.name.toLowerCase())) .sort((a, b) => (a.percentage || 0) - (b.percentage || 0))[0]; const isDisabled = Boolean(account.disabled); // 颜色映射,避免动态类名被 Tailwind purge const getColorClass = (percentage: number) => { const color = getQuotaColor(percentage); switch (color) { case 'success': return 'bg-emerald-500'; case 'warning': return 'bg-amber-500'; case 'error': return 'bg-rose-500'; default: return 'bg-gray-500'; } }; const getTimeColorClass = (resetTime: string | undefined) => { const color = getTimeRemainingColor(resetTime); switch (color) { case 'success': return 'text-emerald-500 dark:text-emerald-400'; case 'warning': return 'text-amber-500 dark:text-amber-400'; default: return 'text-blue-600 dark:text-blue-400'; } }; return ( {/* 序号 */} onSelect()} onClick={(e) => e.stopPropagation()} /> {/* 邮箱 */}
{account.email}
{isCurrent && ( {t('accounts.current').toUpperCase()} )} {isDisabled && ( {t('accounts.disabled')} )} {account.proxy_disabled && ( {t('accounts.proxy_disabled')} )} {account.quota?.is_forbidden && ( {t('accounts.forbidden')} )} {/* 订阅类型徽章 */} {account.quota?.subscription_tier && (() => { const tier = account.quota.subscription_tier.toLowerCase(); if (tier.includes('ultra')) { return ( ULTRA ); } else if (tier.includes('pro')) { return ( PRO ); } else { return ( FREE ); } })()}
{/* 模型配额 */} {account.quota?.is_forbidden ? (
{t('accounts.forbidden_msg')}
) : (
{/* Gemini Pro */}
{geminiProModel && (
)}
{(account.protected_models?.includes('gemini-3-pro-high') || account.protected_models?.includes('gemini-3.1-pro-high')) && } G3.1 Pro
{geminiProModel?.reset_time ? ( {formatTimeRemaining(geminiProModel.reset_time)} ) : ( N/A )}
{geminiProModel ? `${geminiProModel.percentage}%` : '-'}
{/* Gemini Flash */}
{geminiFlashModel && (
)}
{account.protected_models?.includes('gemini-3-flash') && } G3 Flash
{geminiFlashModel?.reset_time ? ( {formatTimeRemaining(geminiFlashModel.reset_time)} ) : ( N/A )}
{geminiFlashModel ? `${geminiFlashModel.percentage}%` : '-'}
{/* Gemini Image */}
{geminiImageModel && (
)}
{account.protected_models?.includes('gemini-3-pro-image') && } G3 Image
{geminiImageModel?.reset_time ? ( {formatTimeRemaining(geminiImageModel.reset_time)} ) : ( N/A )}
{geminiImageModel ? `${geminiImageModel.percentage}%` : '-'}
{/* Claude */}
{claudeModel && (
)}
{account.protected_models?.includes('claude') && } Claude
{claudeModel?.reset_time ? ( {formatTimeRemaining(claudeModel.reset_time)} ) : ( N/A )}
{claudeModel ? `${claudeModel.percentage}%` : '-'}
)} {/* 最后使用 */}
{new Date(account.last_used * 1000).toLocaleDateString()} {new Date(account.last_used * 1000).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
{/* 操作 */}
); } export default AccountRow;