feat: 所有模板 Home 页套餐卡片添加官方价格等价对比

提取 calcOfficialEquiv 为公共工具函数,7 个 Home 模板和
Packages 页面共用,自动选择折扣比例最大的模型展示等价额度。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
abingyydsandClaude Sonnet 4.6 committed 2026-03-25 03:46:01 +09:00
1 parent b33ef787a5
commit 3b8206284f
53 files changed
+255 -199

No files matched your search

+10 -3
View File
@@ -3,7 +3,8 @@ import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useAuth } from '../../context/AuthContext';
import { useSite } from '../../context/SiteContext';
import { getSiteModels, getSitePackages } from '../../api';
import { getSiteModels, getSitePackages, Q } from '../../api';
import { calcOfficialEquiv } from '../../utils/officialEquiv';
import CountUp from '../../components/bits/CountUp';
import FadeContent from '../../components/bits/FadeContent';
@@ -139,7 +140,10 @@ export default function ClaudeHome() {
<p className="text-[#8B7D6E] mb-10">{t('home.choosePlan')}</p>
<div className="grid md:grid-cols-3 gap-4 max-w-4xl">
{packages.filter(p => p.enabled).slice(0, 3).map((pkg, i) => (
{packages.filter(p => p.enabled).slice(0, 3).map((pkg, i) => {
const quotaDollars = pkg.quota_amount > 0 ? pkg.quota_amount / Q : 0;
const equiv = calcOfficialEquiv(enabledModels, quotaDollars);
return (
<div key={pkg.id} className={`rounded-xl p-6 flex flex-col border transition-all ${
i === 1 ? 'border-[#D97757]/30 bg-[#D97757]/[0.04]' : 'border-[#E8DDD0] hover:border-[#D9C5B2]'
}`}>
@@ -153,6 +157,9 @@ export default function ClaudeHome() {
)}
{pkg.duration > 0 && <p className="text-xs text-[#8B7D6E] mt-1">{t('home.days', { count: pkg.duration })}</p>}
</div>
{equiv && equiv.equivDollars > quotaDollars && (
<p className="text-xs text-[#D97757] mt-2">🔥 {t('packages.officialEquiv', { model: equiv.label, amount: Math.round(equiv.equivDollars) })}</p>
)}
<Link to={user ? '/packages' : '/register'} className={`mt-4 py-2.5 rounded-full font-medium text-sm text-center transition-colors ${
i === 1
? 'bg-[#D97757] text-white hover:bg-[#C4613F]'
@@ -161,7 +168,7 @@ export default function ClaudeHome() {
{user ? t('home.subscribe') : t('home.getStarted')}
</Link>
</div>
))}
)})}
</div>
</FadeContent>
</section>
+10 -3
View File
@@ -3,7 +3,8 @@ import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useAuth } from '../../context/AuthContext';
import { useSite } from '../../context/SiteContext';
import { getSiteModels, getSitePackages } from '../../api';
import { getSiteModels, getSitePackages, Q } from '../../api';
import { calcOfficialEquiv } from '../../utils/officialEquiv';
import CountUp from '../../components/bits/CountUp';
import FadeContent from '../../components/bits/FadeContent';
@@ -152,7 +153,10 @@ export default function CleanHome() {
<p className="text-gray-500 mb-10">{t('home.choosePlan')}</p>
<div className="grid md:grid-cols-3 gap-4 max-w-4xl">
{packages.filter(p => p.enabled).slice(0, 3).map((pkg, i) => (
{packages.filter(p => p.enabled).slice(0, 3).map((pkg, i) => {
const quotaDollars = pkg.quota_amount > 0 ? pkg.quota_amount / Q : 0;
const equiv = calcOfficialEquiv(enabledModels, quotaDollars);
return (
<div key={pkg.id} className={`rounded-xl p-6 flex flex-col border transition-all ${
i === 1 ? 'border-blue-200 bg-blue-50/50 shadow-sm' : 'border-gray-100 hover:border-gray-200'
}`}>
@@ -166,6 +170,9 @@ export default function CleanHome() {
)}
{pkg.duration > 0 && <p className="text-xs text-gray-500 mt-1">{t('home.days', { count: pkg.duration })}</p>}
</div>
{equiv && equiv.equivDollars > quotaDollars && (
<p className="text-xs text-amber-600 mt-2">🔥 {t('packages.officialEquiv', { model: equiv.label, amount: Math.round(equiv.equivDollars) })}</p>
)}
<Link to={user ? '/packages' : '/register'} className={`mt-4 py-2.5 rounded-lg font-medium text-sm text-center transition-colors ${
i === 1
? 'bg-blue-600 text-white hover:bg-blue-700'
@@ -174,7 +181,7 @@ export default function CleanHome() {
{user ? t('home.subscribe') : t('home.getStarted')}
</Link>
</div>
))}
)})}
</div>
</FadeContent>
</section>
+10 -3
View File
@@ -3,7 +3,8 @@ import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useAuth } from '../../context/AuthContext';
import { useSite } from '../../context/SiteContext';
import { getSiteModels, getSitePackages } from '../../api';
import { getSiteModels, getSitePackages, Q } from '../../api';
import { calcOfficialEquiv } from '../../utils/officialEquiv';
import CountUp from '../../components/bits/CountUp';
import FadeContent from '../../components/bits/FadeContent';
@@ -151,7 +152,10 @@ export default function CorporateHome() {
<p className="text-slate-500 mb-10">{t('home.choosePlan')}</p>
<div className="grid md:grid-cols-3 gap-5 max-w-4xl">
{packages.filter(p => p.enabled).slice(0, 3).map((pkg, i) => (
{packages.filter(p => p.enabled).slice(0, 3).map((pkg, i) => {
const quotaDollars = pkg.quota_amount > 0 ? pkg.quota_amount / Q : 0;
const equiv = calcOfficialEquiv(enabledModels, quotaDollars);
return (
<div key={pkg.id} className={`rounded-xl p-6 flex flex-col border transition-all ${
i === 1 ? 'border-slate-900 shadow-sm' : 'border-slate-200 hover:border-slate-300'
}`}>
@@ -165,6 +169,9 @@ export default function CorporateHome() {
)}
{pkg.duration > 0 && <p className="text-xs text-slate-500 mt-1">{t('home.days', { count: pkg.duration })}</p>}
</div>
{equiv && equiv.equivDollars > quotaDollars && (
<p className="text-xs text-amber-600 mt-2">🔥 {t('packages.officialEquiv', { model: equiv.label, amount: Math.round(equiv.equivDollars) })}</p>
)}
<Link to={user ? '/packages' : '/register'} className={`mt-4 py-2.5 rounded-lg font-medium text-sm text-center transition-colors ${
i === 1
? 'bg-slate-900 text-white hover:bg-slate-800'
@@ -173,7 +180,7 @@ export default function CorporateHome() {
{user ? t('home.subscribe') : t('home.getStarted')}
</Link>
</div>
))}
)})}
</div>
</FadeContent>
</section>
+10 -3
View File
@@ -3,7 +3,8 @@ import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useAuth } from '../../context/AuthContext';
import { useSite } from '../../context/SiteContext';
import { getSiteModels, getSitePackages } from '../../api';
import { getSiteModels, getSitePackages, Q } from '../../api';
import { calcOfficialEquiv } from '../../utils/officialEquiv';
import CountUp from '../../components/bits/CountUp';
import FadeContent from '../../components/bits/FadeContent';
@@ -142,7 +143,10 @@ export default function DarkHome() {
<p className="text-neutral-500 mb-10">{t('home.choosePlan')}</p>
<div className="grid md:grid-cols-3 gap-4 max-w-4xl">
{packages.filter(p => p.enabled).slice(0, 3).map((pkg, i) => (
{packages.filter(p => p.enabled).slice(0, 3).map((pkg, i) => {
const quotaDollars = pkg.quota_amount > 0 ? pkg.quota_amount / Q : 0;
const equiv = calcOfficialEquiv(enabledModels, quotaDollars);
return (
<div key={pkg.id} className={`rounded-xl p-6 flex flex-col border transition-colors ${
i === 1 ? 'border-emerald-500/25 bg-emerald-500/[0.04]' : 'border-neutral-800/60 hover:border-neutral-700'
}`}>
@@ -156,6 +160,9 @@ export default function DarkHome() {
)}
{pkg.duration > 0 && <p className="text-xs text-neutral-600 mt-1">{t('home.days', { count: pkg.duration })}</p>}
</div>
{equiv && equiv.equivDollars > quotaDollars && (
<p className="text-xs text-amber-300/80 mt-2">🔥 {t('packages.officialEquiv', { model: equiv.label, amount: Math.round(equiv.equivDollars) })}</p>
)}
<Link to={user ? '/packages' : '/register'} className={`mt-4 py-2.5 rounded-lg font-medium text-sm text-center transition-colors ${
i === 1
? 'bg-emerald-500 text-black hover:bg-emerald-400'
@@ -164,7 +171,7 @@ export default function DarkHome() {
{user ? t('home.subscribe') : t('home.getStarted')}
</Link>
</div>
))}
)})}
</div>
</FadeContent>
</section>
+10 -3
View File
@@ -3,7 +3,8 @@ import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useAuth } from '../../context/AuthContext';
import { useSite } from '../../context/SiteContext';
import { getSiteModels, getSitePackages } from '../../api';
import { getSiteModels, getSitePackages, Q } from '../../api';
import { calcOfficialEquiv } from '../../utils/officialEquiv';
import CountUp from '../../components/bits/CountUp';
import FadeContent from '../../components/bits/FadeContent';
@@ -151,7 +152,10 @@ export default function DefaultHome() {
<p className="text-neutral-500 mb-10">{t('home.choosePlan')}</p>
<div className="grid md:grid-cols-3 gap-4 max-w-4xl">
{packages.filter(p => p.enabled).slice(0, 3).map((pkg, i) => (
{packages.filter(p => p.enabled).slice(0, 3).map((pkg, i) => {
const quotaDollars = pkg.quota_amount > 0 ? pkg.quota_amount / Q : 0;
const equiv = calcOfficialEquiv(enabledModels, quotaDollars);
return (
<div key={pkg.id} className={`rounded-2xl p-6 flex flex-col border transition-colors ${
i === 1 ? 'bg-white/[0.04] border-white/[0.12]' : 'bg-white/[0.02] border-white/[0.06] hover:border-white/[0.1]'
}`}>
@@ -165,6 +169,9 @@ export default function DefaultHome() {
)}
{pkg.duration > 0 && <p className="text-xs text-neutral-600 mt-1">{t('home.days', { count: pkg.duration })}</p>}
</div>
{equiv && equiv.equivDollars > quotaDollars && (
<p className="text-xs text-amber-300/80 mt-2">🔥 {t('packages.officialEquiv', { model: equiv.label, amount: Math.round(equiv.equivDollars) })}</p>
)}
<Link to={user ? '/packages' : '/register'} className={`mt-4 py-2.5 rounded-xl font-medium text-sm text-center transition-colors ${
i === 1
? 'bg-white text-black hover:bg-neutral-100'
@@ -173,7 +180,7 @@ export default function DefaultHome() {
{user ? t('home.subscribe') : t('home.getStarted')}
</Link>
</div>
))}
)})}
</div>
</FadeContent>
</section>
+10 -3
View File
@@ -3,7 +3,8 @@ import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useAuth } from '../../context/AuthContext';
import { useSite } from '../../context/SiteContext';
import { getSiteModels, getSitePackages } from '../../api';
import { getSiteModels, getSitePackages, Q } from '../../api';
import { calcOfficialEquiv } from '../../utils/officialEquiv';
import CountUp from '../../components/bits/CountUp';
import FadeContent from '../../components/bits/FadeContent';
@@ -137,7 +138,10 @@ export default function MinimalHome() {
<p className="text-neutral-500 mb-10">{t('home.choosePlan')}</p>
<div className="grid md:grid-cols-3 gap-4 max-w-4xl">
{packages.filter(p => p.enabled).slice(0, 3).map((pkg) => (
{packages.filter(p => p.enabled).slice(0, 3).map((pkg) => {
const quotaDollars = pkg.quota_amount > 0 ? pkg.quota_amount / Q : 0;
const equiv = calcOfficialEquiv(enabledModels, quotaDollars);
return (
<div key={pkg.id} className="rounded-xl p-6 flex flex-col border border-neutral-800/60 hover:border-neutral-700 transition-colors">
<h3 className="text-base font-semibold text-white">{pkg.name}</h3>
{pkg.description && <p className="text-sm text-neutral-500 mt-1">{pkg.description}</p>}
@@ -148,11 +152,14 @@ export default function MinimalHome() {
)}
{pkg.duration > 0 && <p className="text-xs text-neutral-600 mt-1">{t('home.days', { count: pkg.duration })}</p>}
</div>
{equiv && equiv.equivDollars > quotaDollars && (
<p className="text-xs text-amber-300/80 mt-2">🔥 {t('packages.officialEquiv', { model: equiv.label, amount: Math.round(equiv.equivDollars) })}</p>
)}
<Link to={user ? '/packages' : '/register'} className="mt-4 py-2.5 rounded-lg bg-neutral-800 text-white font-medium text-sm text-center hover:bg-neutral-700 transition-colors">
{user ? t('home.subscribe') : t('home.getStarted')}
</Link>
</div>
))}
)})}
</div>
</FadeContent>
</section>
+10 -3
View File
@@ -3,7 +3,8 @@ import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useAuth } from '../../context/AuthContext';
import { useSite } from '../../context/SiteContext';
import { getSiteModels, getSitePackages } from '../../api';
import { getSiteModels, getSitePackages, Q } from '../../api';
import { calcOfficialEquiv } from '../../utils/officialEquiv';
import CountUp from '../../components/bits/CountUp';
import FadeContent from '../../components/bits/FadeContent';
@@ -157,7 +158,10 @@ export default function StarterHome() {
<p className="text-neutral-500 mb-10">{t('home.choosePlan')}</p>
<div className="grid md:grid-cols-3 gap-4 max-w-4xl">
{packages.filter(p => p.enabled).slice(0, 3).map((pkg, i) => (
{packages.filter(p => p.enabled).slice(0, 3).map((pkg, i) => {
const quotaDollars = pkg.quota_amount > 0 ? pkg.quota_amount / Q : 0;
const equiv = calcOfficialEquiv(enabledModels, quotaDollars);
return (
<div key={pkg.id} className={`rounded-xl p-6 flex flex-col border transition-colors ${
i === 1 ? 'border-brand-500/30 bg-brand-500/[0.04]' : 'border-neutral-800/60 hover:border-neutral-700'
}`}>
@@ -171,6 +175,9 @@ export default function StarterHome() {
)}
{pkg.duration > 0 && <p className="text-xs text-neutral-600 mt-1">{t('home.days', { count: pkg.duration })}</p>}
</div>
{equiv && equiv.equivDollars > quotaDollars && (
<p className="text-xs text-amber-300/80 mt-2">🔥 {t('packages.officialEquiv', { model: equiv.label, amount: Math.round(equiv.equivDollars) })}</p>
)}
<Link to={user ? '/packages' : '/register'} className={`mt-4 py-2.5 rounded-lg font-medium text-sm text-center transition-colors ${
i === 1
? 'bg-brand-600 text-white hover:bg-brand-500'
@@ -179,7 +186,7 @@ export default function StarterHome() {
{user ? t('home.subscribe') : t('home.getStarted')}
</Link>
</div>
))}
)})}
</div>
</FadeContent>
</section>