Files
pangolin/web/website/src/components/Pricing.astro
T
wangjia 70d3293251 refactor(website): 域名/邮箱/渠道收口到 src/config/site.ts 单一真相源
新增 SITE 配置(url/email/telegram/line/store),Footer、Pricing、astro.config
(canonical 域名)均改为引用它;改联系方式/域名只动一处。
astro.config.mjs 直接 import 该 TS(Astro 配置加载支持)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 19:23:19 +08:00

121 lines
3.8 KiB
Plaintext

---
import Icon from './Icon.astro';
import PricingPlans from './PricingPlans.jsx';
import { PRICES, type T, type Lang } from '../i18n/strings';
import { SITE } from '../config/site';
interface Props { t: T; lang: Lang }
const { t, lang } = Astro.props;
const P = PRICES[lang];
const plansData = {
monthly: t('price.monthly'),
yearly: t('price.yearly'),
save: t('price.save'),
permo: t('price.permo'),
popular: t('price.popular'),
plans: [
{
key: 'free',
name: t('price.free'),
price: P.free,
desc: t('price.freedesc'),
feats: [t('pf.free1'), t('pf.free2'), t('pf.free3'), t('pf.free4')],
cta: t('price.cta_free'),
ctaClass: 'pcta-out',
highlight: false,
},
{
key: 'pro',
name: t('price.pro'),
price: P.pro,
desc: t('price.prodesc'),
feats: [t('pf.pro1'), t('pf.pro2'), t('pf.pro3'), t('pf.pro4'), t('pf.pro5')],
cta: t('price.cta_pro'),
ctaClass: 'pcta-white',
highlight: true,
},
{
key: 'team',
name: t('price.team'),
price: P.team,
desc: t('price.teamdesc'),
feats: [t('pf.team1'), t('pf.team2'), t('pf.team3'), t('pf.team4')],
cta: t('price.cta_team'),
ctaClass: 'pcta-fill',
highlight: false,
},
],
};
// 功能对比表行:[名称键, free, pro, team];值可为字符串或 'check' / 'dash'。
const rows = [
{ label: 'cmp.locations', free: '1', pro: t('cmp.loc_all'), team: t('cmp.loc_all') },
{ label: 'cmp.data', free: t('cmp.daily'), pro: t('cmp.unlimited'), team: t('cmp.unlimited') },
{ label: 'cmp.time', free: t('cmp.time_free'), pro: t('cmp.unlimited'), team: t('cmp.unlimited') },
{ label: 'cmp.devices', free: '1', pro: '5', team: '10' },
{ label: 'cmp.speed', free: 'dash', pro: 'check', team: 'check' },
{ label: 'cmp.stream', free: 'dash', pro: 'check', team: 'check' },
{ label: 'cmp.kill', free: 'dash', pro: 'check', team: 'check' },
{ label: 'cmp.support', free: t('cmp.basic'), pro: t('cmp.basic'), team: t('cmp.priority') },
];
function cell(v: string) {
return v === 'check' ? 'yes' : v === 'dash' ? 'no' : 'tx';
}
---
<section id="pricing">
<div class="wrap center">
<div class="eyebrow">{t('price.eyebrow')}</div>
<h2 class="h-sec">{t('price.h')}</h2>
<p class="sub-sec">{t('price.sub')}</p>
</div>
<PricingPlans client:visible data={plansData} />
<div class="wrap">
<!-- payment note -->
<div class="pay-note">
<Icon name="shield-check" />
<div>
<b>{t('pay.title')}</b><br>
<span>{t('pay.body')}</span>
<div class="chips">
<span class="chip"><Icon name="shopping-bag" /><span>{t('pay.store')}</span></span>
<span class="chip"><Icon name="credit-card" /><span>{t('pay.usdt')}</span></span>
<a class="chip" href={SITE.telegram.url} target="_blank" rel="noopener"><Icon name="send" />Telegram {SITE.telegram.handle}</a>
<span class="chip"><Icon name="message-circle" />LINE {SITE.line.handle}</span>
<a class="chip" href={`mailto:${SITE.email}`}><Icon name="mail" />{SITE.email}</a>
</div>
</div>
</div>
<!-- comparison table -->
<h3 class="cmp-h">{t('cmp.h')}</h3>
<div class="cmp">
<table>
<thead>
<tr>
<th>{t('cmp.feature')}</th>
<th>{t('price.free')}</th>
<th>{t('price.pro')}</th>
<th>{t('price.team')}</th>
</tr>
</thead>
<tbody>
{rows.map((r) => (
<tr>
<td>{t(r.label)}</td>
{[r.free, r.pro, r.team].map((v) => (
<td class={cell(v)}>
{v === 'check' ? <Icon name="check" /> : v === 'dash' ? '—' : v}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
</div>
</section>