'use client'; import { useEffect, useState } from 'react'; import { Skeleton } from '@/components/ui/skeleton'; import { API_BASE } from '@/lib/config'; import { useT } from '@/i18n'; interface AiModel { id: number; name: string; provider: string; description: string | null; capabilities: string | null; contextWindow: number | null; maxTokens: number | null; pricing: string | null; isFree: boolean; isFeatured: boolean; icon: string | null } export default function ModelsPage() { const t = useT(); const [models, setModels] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { fetch(`${API_BASE}/models`).then(r => r.json()).then(setModels).catch(() => {}).finally(() => setLoading(false)); }, []); if (loading) { return (
{[1,2,3,4,5].map(i => )}
); } return (

{t.models.title}

{t.models.desc}

{models.map((model) => ( ))}
{t.models.tableName} {t.models.tableProvider} {t.models.tableCapabilities} {t.models.tableContext} {t.models.tableMaxOutput} {t.models.tablePricing}
{model.name}
{model.isFree && {t.models.free}} {model.isFeatured && !model.isFree && {t.models.recommended}}
{model.provider}
{model.capabilities?.split(',').map(cap => ( {cap.trim()} ))}
{model.contextWindow ? `${(model.contextWindow / 1000).toFixed(0)}K` : '-'} {model.maxTokens ? `${(model.maxTokens / 1024).toFixed(0)}K` : '-'} {model.isFree ? t.models.pricingFree : model.pricing?.includes('免费') ? t.models.pricingMixed : t.models.pricingPaid}
{models.length > 0 && (
{models.filter(m => m.isFeatured).map((model) => (

{model.name}

{model.isFree && {t.models.free}}

{model.provider}

{model.description}

{model.capabilities?.split(',').slice(0, 4).map(cap => ( {cap.trim()} ))}
{t.models.contextWindow}{model.contextWindow ? `${(model.contextWindow / 1000).toFixed(0)}K tokens` : '-'}
{t.models.maxOutput}{model.maxTokens ? `${(model.maxTokens / 1024).toFixed(0)}K tokens` : '-'}
))}
)}
); }