feat(web): iOS/iPad 下载卡合一 + 双按钮 + 正式版「?」气泡提示

- 三张 iOS/iPad 卡合成一张:测试版(TestFlight,实心可点)+ 正式版(App Store,描边禁用)双按钮。
- 「?」移入正式版按钮内,自定义 tooltip 气泡(走设计 token,上浮淡入+小箭头)替代原生 title,
  悬浮/聚焦显示「美区即将上线 · 需美区 Apple ID」;光标不再变问号。
- 新增 i18n 文案 dl.us_soon(6 语)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-09-11 16:33:16 +08:00
parent a913fe56fb
commit a392d18a6a
2 changed files with 67 additions and 7 deletions
+66 -7
View File
@@ -7,11 +7,17 @@ interface Props { t: T }
const { t } = Astro.props;
// href 缺省 = 未接入下载 → 禁用「敬请期待」占位。
// iOS/iPad 目前仅「测试版」(TestFlight 公开链接,需美区 Apple ID);「正式版」(App Store)敬请期待。
const plats: { icon: string; name: string; ver: string; href?: string; badge?: string; note?: string }[] = [
{ icon: 'smartphone', name: 'iOS', ver: 'iOS 15+', href: SITE.downloads.ios, badge: t('dl.beta'), note: t('dl.us_id') },
{ icon: 'tablet', name: 'iPad', ver: 'iPadOS 15+', href: SITE.downloads.ios, badge: t('dl.beta'), note: t('dl.us_id') },
{ icon: 'smartphone', name: 'iOS / iPad', ver: 'App Store', badge: t('dl.stable') },
// iOS/iPad 合成一张卡、两个按钮:「测试版」(TestFlight 公开链接) 现已可装,
// 「正式版」(App Store) 禁用,按钮内带「?」——悬浮提示「美区即将上线,需美区 Apple ID」。
type Action = { label: string; href?: string; hint?: string };
const plats: { icon: string; name: string; ver: string; href?: string; badge?: string; note?: string; actions?: Action[] }[] = [
{
icon: 'smartphone', name: 'iOS / iPad', ver: 'iOS / iPadOS 15+',
actions: [
{ label: t('dl.beta'), href: SITE.downloads.ios },
{ label: t('dl.stable'), hint: `${t('dl.us_soon')} · ${t('dl.us_id')}` },
],
},
{ icon: 'smartphone', name: 'Android', ver: 'Android 9+', href: SITE.downloads.android },
{ icon: 'laptop', name: 'macOS', ver: 'macOS 12+', href: SITE.downloads.macos },
{ icon: 'monitor', name: 'Windows', ver: 'Win 10/11', href: SITE.downloads.windows },
@@ -35,7 +41,24 @@ const plats: { icon: string; name: string; ver: string; href?: string; badge?: s
{p.note && <span class="q" title={p.note} tabindex="0" aria-label={p.note}>?</span>}
</div>
<div class="pv">{p.ver}</div>
{p.href ? (
{p.actions ? (
<div class="btns">
{p.actions.map((a) => (
a.href ? (
<a class="b2 beta" href={a.href}><span>{a.label}</span></a>
) : (
<span class="b2 disabled" aria-disabled="true">
<span>{a.label}</span>
{a.hint && (
<span class="q" tabindex="0" aria-label={a.hint}>
?<span class="tip" role="tooltip">{a.hint}</span>
</span>
)}
</span>
)
))}
</div>
) : p.href ? (
<a class="gb" href={p.href}><Icon name="download" /><span>{t('dl.get')}</span></a>
) : (
<span class="gb disabled" aria-disabled="true"><Icon name="download" /><span>{t('dl.soon')}</span></span>
@@ -56,9 +79,45 @@ const plats: { icon: string; name: string; ver: string; href?: string; badge?: s
font-family: var(--font-mono); letter-spacing: .02em;
}
.dl .q {
position: relative;
display: inline-flex; align-items: center; justify-content: center;
width: 16px; height: 16px; border-radius: 50%;
font-size: 11px; font-weight: 700; cursor: help;
font-size: 11px; font-weight: 700; cursor: default;
background: var(--bg-subtle); color: var(--fg3);
}
/* 自定义提示气泡:走设计 token,悬浮/聚焦「?」时上浮显示,带小箭头。 */
.dl .q .tip {
position: absolute; bottom: calc(100% + 9px); left: 50%;
transform: translateX(-50%) translateY(4px);
width: max-content; max-width: 210px; white-space: normal;
padding: 8px 11px; border-radius: var(--radius-md, 10px);
background: var(--fg1); color: var(--bg);
font-family: var(--font-sans); font-size: 12px; font-weight: 500;
line-height: 1.45; text-align: center; letter-spacing: 0;
box-shadow: var(--shadow-md, 0 6px 20px rgba(45,30,20,.18));
opacity: 0; visibility: hidden; pointer-events: none; z-index: 20;
transition: opacity var(--dur-base, .18s) var(--ease-out, ease),
transform var(--dur-base, .18s) var(--ease-out, ease),
visibility var(--dur-base, .18s);
}
.dl .q .tip::after {
content: ""; position: absolute; top: 100%; left: 50%;
transform: translateX(-50%);
border: 5px solid transparent; border-top-color: var(--fg1);
}
.dl .q:hover .tip, .dl .q:focus-visible .tip {
opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0);
}
/* iOS/iPad 单卡双按钮:测试版(实心 accent 可点)+ 正式版(描边禁用,内含「?」悬浮提示)。 */
.dl .btns { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-top: 2px; }
.dl .b2 {
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
padding: 8px 15px; border-radius: 999px; line-height: 1.15;
font-size: 13px; font-weight: 600; text-decoration: none;
border: 1px solid var(--accent); color: var(--accent);
}
.dl .b2.beta { background: var(--accent); color: var(--fg-on-accent); border-color: var(--accent); }
/* 禁用态:不导航(本就是 span)+ 默认光标,但不禁用指针事件——否则内含「?」的 title 悬浮失效。 */
.dl .b2.disabled { border-color: var(--border); color: var(--fg3); cursor: default; }
.dl .b2 .q { background: transparent; border: 1px solid var(--border); }
</style>
+1
View File
@@ -140,6 +140,7 @@ export const STRINGS: Record<string, Record<Lang, string>> = {
'dl.beta': { zh: '测试版', en: 'Beta', ja: 'ベータ版', ko: '베타', ru: 'Бета', es: 'Beta' },
'dl.stable': { zh: '正式版', en: 'Stable', ja: '正式版', ko: '정식판', ru: 'Релиз', es: 'Estable' },
'dl.us_id': { zh: '需美区 Apple ID', en: 'Requires a US Apple ID', ja: '米国 Apple ID が必要', ko: '미국 Apple ID 필요', ru: 'Нужен Apple ID (США)', es: 'Requiere un Apple ID de EE. UU.' },
'dl.us_soon': { zh: '美区即将上线', en: 'Coming to US App Store', ja: '米国 App Store で近日公開', ko: '미국 App Store 출시 예정', ru: 'Скоро в App Store (США)', es: 'Pronto en App Store (EE. UU.)' },
'docs.eyebrow': { zh: '文档', en: 'Docs', ja: 'ドキュメント', ko: '문서', ru: 'Документация', es: 'Documentación' },
'docs.h': { zh: '需要帮助?都在这里', en: 'Need help? Its all here', ja: 'お困りですか?すべてここに', ko: '도움이 필요하세요? 모두 여기에', ru: 'Нужна помощь? Всё здесь', es: '¿Necesitas ayuda? Todo está aquí' },