feat(site): 官网全新重建(App 真相源风格)+ 四档套餐 + 在线购买流
- tokens.css/site.css 逐值对齐原型设计系统,icons 雪碧图,零外链;首页/下载/帮助/法务全站重皮肤,真实截图 - 注册与登录在官网内完成(写 shared_preferences_web 兼容 localStorage,不再跳 App) - 价格区四档:免费/标准 299/高级 599/定制;登录后标准/高级按钮变「购买 / 续费」 - 新增 /checkout/(套餐+月付年付切换)与 /license/result/(支付回跳轮询到账) - 登录页支持 ?next= 站内回跳;二维码措辞改「扫码看详情可分享」 - 下载页构建期真实下载链接+运行时 release 刷新;旧 features 页/死 CSS/dist 产物清理 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
This commit is contained in:
+130
-456
@@ -1,397 +1,113 @@
|
||||
---
|
||||
layout: base.njk
|
||||
title: 下载
|
||||
description: 下载岩美酒库管理系统,Web 版现已上线,Windows / macOS / iOS / Android 客户端即将推出。
|
||||
description: 下载岩美酒库管理系统客户端:Windows、macOS、Android、iOS 与免安装 Web 版。
|
||||
pageStyle: |
|
||||
/* ── Page header ── */
|
||||
.page-header {
|
||||
padding: 72px 0 56px;
|
||||
background:
|
||||
radial-gradient(700px 360px at 80% 20%, rgba(37, 99, 172, 0.06), transparent 60%),
|
||||
linear-gradient(180deg, #FBFCFD 0%, #FFFFFF 80%);
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
}
|
||||
.ph-inner {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 64px;
|
||||
align-items: center;
|
||||
}
|
||||
.ph-eyebrow {
|
||||
font-size: var(--text-xs); font-weight: 600;
|
||||
letter-spacing: 0.12em; color: var(--brand-500);
|
||||
text-transform: uppercase;
|
||||
margin: 0 0 12px;
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
}
|
||||
.ph-eyebrow .version-badge {
|
||||
background: var(--brand-500); color: #fff;
|
||||
padding: 3px 8px; border-radius: var(--radius-sm);
|
||||
font-family: var(--font-mono);
|
||||
letter-spacing: 0.02em; font-size: 11px;
|
||||
}
|
||||
.ph-title {
|
||||
font-size: 44px; font-weight: 700; line-height: 1.15;
|
||||
letter-spacing: var(--tracking-cn-display);
|
||||
color: var(--brand-900); margin: 0 0 18px;
|
||||
}
|
||||
.ph-lead {
|
||||
font-size: var(--text-lg); color: var(--gray-600);
|
||||
line-height: 1.6; margin: 0 0 24px; max-width: 480px;
|
||||
}
|
||||
.ph-meta {
|
||||
display: flex; gap: 24px;
|
||||
font-size: var(--text-sm); color: var(--fg-muted);
|
||||
border-top: 1px solid var(--border-subtle);
|
||||
padding-top: 16px;
|
||||
}
|
||||
.ph-meta-item { display: flex; flex-direction: column; gap: 2px; }
|
||||
.ph-meta-label { font-size: 11px; color: var(--fg-subtle); letter-spacing: 0.04em; }
|
||||
.ph-meta-val { font-size: var(--text-sm); color: var(--gray-800); font-weight: 500; }
|
||||
|
||||
/* ── Auto-detect card ── */
|
||||
.detect-card {
|
||||
background: var(--gray-0);
|
||||
border: 1px solid var(--border-default);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-md);
|
||||
overflow: hidden;
|
||||
}
|
||||
.detect-card-head {
|
||||
padding: 14px 20px;
|
||||
background: var(--gray-50);
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
font-size: var(--text-xs); color: var(--fg-muted);
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
}
|
||||
.detect-card-head .head-left { display: flex; align-items: center; gap: 6px; }
|
||||
.detect-card-head .head-left svg { width: 14px; height: 14px; color: var(--success-500); }
|
||||
.detect-card-body {
|
||||
padding: 28px;
|
||||
display: flex; gap: 24px; align-items: center;
|
||||
}
|
||||
.detect-icon {
|
||||
width: 72px; height: 72px;
|
||||
background: var(--brand-50); border-radius: 16px;
|
||||
display: grid; place-items: center;
|
||||
color: var(--brand-500); flex-shrink: 0;
|
||||
}
|
||||
.detect-icon svg { width: 38px; height: 38px; }
|
||||
.detect-info { flex: 1; }
|
||||
.detect-info h3 { margin: 0 0 4px; font-size: var(--text-xl); font-weight: 600; color: var(--brand-900); }
|
||||
.detect-info p { margin: 0; font-size: var(--text-sm); color: var(--fg-muted); }
|
||||
.detect-card-actions { padding: 0 28px 28px; display: flex; gap: 8px; }
|
||||
.detect-card-actions .btn-primary { flex: 1; justify-content: center; }
|
||||
|
||||
/* ── Platform grid ── */
|
||||
.dl-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 12px;
|
||||
margin-top: 32px;
|
||||
}
|
||||
.dl-card {
|
||||
background: var(--gray-0);
|
||||
border: 1px solid var(--border-default);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 20px;
|
||||
display: flex; flex-direction: column;
|
||||
transition: border-color var(--duration-base) var(--ease-standard),
|
||||
box-shadow var(--duration-base) var(--ease-standard);
|
||||
}
|
||||
.dl-card:hover { border-color: var(--brand-300); box-shadow: var(--shadow-sm); }
|
||||
.dl-card.detected { border-color: var(--brand-400); box-shadow: var(--shadow-md); }
|
||||
.dl-card.coming-soon { opacity: 0.65; }
|
||||
.dl-card-icon {
|
||||
width: 48px; height: 48px;
|
||||
background: var(--brand-50); border-radius: var(--radius-md);
|
||||
display: grid; place-items: center; color: var(--brand-500);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.dl-card-icon svg { width: 24px; height: 24px; }
|
||||
.dl-card-name { font-size: var(--text-md); font-weight: 600; color: var(--brand-900); margin: 0 0 4px; }
|
||||
.dl-card-desc { font-size: var(--text-xs); color: var(--fg-muted); margin: 0 0 14px; line-height: 1.5; flex: 1; }
|
||||
.dl-status {
|
||||
display: inline-flex; align-items: center; gap: 4px;
|
||||
font-size: 10px; font-weight: 600; letter-spacing: 0.04em;
|
||||
padding: 3px 8px; border-radius: var(--radius-pill); margin-bottom: 12px;
|
||||
}
|
||||
.dl-status.available { background: var(--success-50); color: var(--success-700); }
|
||||
.dl-status.soon { background: var(--gray-100); color: var(--fg-muted); }
|
||||
.dl-card-btn {
|
||||
display: flex; align-items: center; justify-content: center; gap: 6px;
|
||||
height: 34px; padding: 0 14px;
|
||||
border-radius: var(--radius-md);
|
||||
font-size: var(--text-sm); font-weight: 500;
|
||||
border: 1px solid transparent;
|
||||
cursor: pointer; width: 100%;
|
||||
transition: background var(--duration-fast) var(--ease-standard);
|
||||
text-decoration: none;
|
||||
}
|
||||
.dl-card-btn.primary { background: var(--brand-500); color: #fff; }
|
||||
.dl-card-btn.primary:hover { background: var(--brand-600); }
|
||||
.dl-card-btn.disabled {
|
||||
background: var(--gray-100); color: var(--fg-muted);
|
||||
cursor: default; pointer-events: none;
|
||||
border-color: var(--border-subtle);
|
||||
}
|
||||
.dl-card-btn svg { width: 14px; height: 14px; }
|
||||
|
||||
/* ── System requirements (rendered markdown) ── */
|
||||
.sysreq-content h2 {
|
||||
font-size: var(--text-xl); font-weight: 600;
|
||||
color: var(--brand-900); margin: 28px 0 12px;
|
||||
letter-spacing: var(--tracking-cn-display);
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
}
|
||||
.sysreq-content h2:first-child { margin-top: 0; }
|
||||
.sysreq-content table {
|
||||
width: 100%; border-collapse: collapse;
|
||||
font-size: var(--text-sm); color: var(--gray-800);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.sysreq-content th {
|
||||
text-align: left; font-size: 11px; font-weight: 600;
|
||||
letter-spacing: 0.04em; text-transform: uppercase;
|
||||
color: var(--fg-muted); padding: 8px 12px;
|
||||
background: var(--gray-50);
|
||||
border: 1px solid var(--border-subtle);
|
||||
}
|
||||
.sysreq-content td {
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--border-subtle);
|
||||
vertical-align: top;
|
||||
}
|
||||
.sysreq-content tr:hover td { background: var(--gray-25); }
|
||||
.sysreq-content p { font-size: var(--text-sm); color: var(--fg-muted); margin: 8px 0 0; }
|
||||
|
||||
/* ── Changelog timeline ── */
|
||||
.changelog {
|
||||
display: grid;
|
||||
grid-template-columns: 180px 1fr;
|
||||
gap: 0;
|
||||
margin-top: 40px;
|
||||
border-left: 1px solid var(--border-subtle);
|
||||
}
|
||||
.changelog-entry { display: contents; }
|
||||
.changelog-meta {
|
||||
padding: 28px 24px 28px 32px;
|
||||
position: relative;
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
}
|
||||
.changelog-meta::before {
|
||||
content: '';
|
||||
position: absolute; left: -7px; top: 36px;
|
||||
width: 13px; height: 13px; border-radius: 50%;
|
||||
background: var(--gray-0); border: 3px solid var(--gray-300);
|
||||
}
|
||||
.changelog-entry.current .changelog-meta::before {
|
||||
background: var(--brand-500); border-color: var(--brand-100);
|
||||
box-shadow: 0 0 0 4px var(--brand-50);
|
||||
}
|
||||
.changelog-version {
|
||||
font-family: var(--font-mono); font-size: var(--text-md); font-weight: 600;
|
||||
color: var(--brand-900); margin-bottom: 4px;
|
||||
}
|
||||
.changelog-date { font-size: var(--text-xs); color: var(--fg-muted); }
|
||||
.changelog-tag {
|
||||
display: inline-block; font-size: 10px;
|
||||
background: var(--success-50); color: var(--success-700);
|
||||
padding: 2px 8px; border-radius: var(--radius-pill);
|
||||
font-weight: 500; margin-top: 8px;
|
||||
}
|
||||
.changelog-body {
|
||||
padding: 28px 0 28px 32px;
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
border-left: 1px solid var(--border-subtle);
|
||||
margin-left: -1px;
|
||||
}
|
||||
.changelog-entry:last-child .changelog-meta,
|
||||
.changelog-entry:last-child .changelog-body { border-bottom: none; }
|
||||
.changelog-intro {
|
||||
font-size: var(--text-sm); color: var(--gray-700);
|
||||
line-height: 1.6; margin: 0 0 16px;
|
||||
}
|
||||
.changelog-cat { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
|
||||
.changelog-cat-label {
|
||||
font-size: 11px; font-weight: 600; letter-spacing: 0.06em;
|
||||
color: var(--fg-muted); text-transform: uppercase;
|
||||
display: flex; align-items: center; gap: 6px; margin-bottom: 2px;
|
||||
}
|
||||
.changelog-cat-label .dot { width: 6px; height: 6px; border-radius: 50%; }
|
||||
.dot-feature { background: var(--brand-500); }
|
||||
.dot-improve { background: var(--success-500); }
|
||||
.dot-fix { background: var(--warning-500); }
|
||||
.changelog-cat ul {
|
||||
margin: 0; padding: 0; list-style: none;
|
||||
font-size: var(--text-sm); color: var(--gray-700); line-height: 1.6;
|
||||
}
|
||||
.changelog-cat ul li {
|
||||
padding: 3px 0 3px 16px; position: relative;
|
||||
}
|
||||
.changelog-cat ul li::before {
|
||||
content: ''; position: absolute; left: 0; top: 11px;
|
||||
width: 6px; height: 1px; background: var(--gray-400);
|
||||
}
|
||||
|
||||
/* ── Responsive ── */
|
||||
@media (max-width: 1024px) {
|
||||
.ph-inner { grid-template-columns: 1fr; gap: 32px; }
|
||||
.ph-title { font-size: 36px; }
|
||||
.dl-grid { grid-template-columns: repeat(3, 1fr); }
|
||||
.changelog { grid-template-columns: 120px 1fr; }
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.dl-grid { grid-template-columns: repeat(2, 1fr); }
|
||||
.page-header { padding: 48px 0 40px; }
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.ph-meta { flex-wrap: wrap; gap: 12px; }
|
||||
.dl-grid { grid-template-columns: 1fr; }
|
||||
.changelog { grid-template-columns: 1fr; }
|
||||
.changelog-version { font-size: 13px; }
|
||||
}
|
||||
.dl-hero{background:var(--mk-hero-grad); color:var(--on-accent); padding:64px 0 72px; text-align:center;}
|
||||
.dl-hero h1{color:var(--on-accent); font-size:var(--mk-h2); font-weight:800;}
|
||||
.dl-hero p{color:var(--on-accent-faint); margin:12px auto 0; max-width:36em;}
|
||||
.detect{max-width:640px; margin:-40px auto 0; position:relative; z-index:2;}
|
||||
.detect .card{display:flex; align-items:center; gap:18px; box-shadow:var(--sh-2); padding:20px 22px;}
|
||||
.detect .icbox{width:44px; height:44px; border-radius:var(--r-lg); flex:none; display:flex; align-items:center; justify-content:center;}
|
||||
.detect .icbox .ic{width:22px; height:22px;}
|
||||
.detect b{display:block; color:var(--heading); font-size:16px;}
|
||||
.detect span{color:var(--muted); font-size:var(--fs-sm);}
|
||||
.detect .btn{margin-left:auto; flex:none;}
|
||||
.dl-grid{display:grid; grid-template-columns:repeat(5,1fr); gap:14px; margin-top:46px;}
|
||||
.dl-card{background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg); padding:20px 18px; display:flex; flex-direction:column; text-align:center;}
|
||||
.dl-card.detected{border-color:var(--primary); box-shadow:var(--sh-1);}
|
||||
.dl-card .icbox{width:38px; height:38px; border-radius:var(--r-md); margin:0 auto 12px; display:flex; align-items:center; justify-content:center;}
|
||||
.dl-card h3{font-size:15px; font-weight:700; margin-bottom:4px;}
|
||||
.dl-card .req{color:var(--faint); font-size:var(--fs-xs); margin-bottom:14px;}
|
||||
.dl-card .dl-status{font-size:var(--fs-xs); color:var(--success); margin-bottom:8px;}
|
||||
.dl-card .dl-status.soon{color:var(--warn);}
|
||||
.dl-card .btn{margin-top:auto; width:100%;}
|
||||
.btn.disabled{opacity:.55; pointer-events:none; background:var(--bg); border:1px solid var(--border); color:var(--muted);}
|
||||
/* 更新日志时间线 */
|
||||
.changelog{max-width:820px; margin:0 auto;}
|
||||
.changelog-entry{display:grid; grid-template-columns:150px 1fr; gap:20px; padding:22px 0; border-bottom:1px solid var(--border-subtle);}
|
||||
.changelog-version{font-family:var(--font-mono); font-weight:700; color:var(--heading); font-size:16px;}
|
||||
.changelog-date{color:var(--faint); font-size:var(--fs-sm); margin-top:3px;}
|
||||
.changelog-tag{display:inline-block; margin-top:8px; background:var(--info-soft); color:var(--primary); font-size:var(--fs-xs); font-weight:700; padding:2px 9px; border-radius:var(--r-pill);}
|
||||
.changelog-cat-label{display:flex; align-items:center; gap:7px; font-size:var(--fs-sm); font-weight:700; color:var(--heading); margin:10px 0 6px;}
|
||||
.changelog-cat-label .dot{width:7px; height:7px; border-radius:50%;}
|
||||
.dot-feature{background:var(--primary);} .dot-improve{background:var(--success);} .dot-fix{background:var(--warn);}
|
||||
.changelog-body ul{margin:0; padding-left:20px; color:var(--muted); font-size:14px; line-height:1.8;}
|
||||
.changelog-intro{color:var(--muted); font-size:14px; margin:0 0 4px;}
|
||||
@media (max-width:900px){ .dl-grid{grid-template-columns:repeat(2,1fr);} }
|
||||
@media (max-width:600px){ .dl-grid{grid-template-columns:1fr;} .detect .card{flex-wrap:wrap;} .changelog-entry{grid-template-columns:1fr;} }
|
||||
---
|
||||
|
||||
<!-- ============================== PAGE HEADER ============================== -->
|
||||
<section class="page-header">
|
||||
<div class="container ph-inner">
|
||||
<header class="dl-hero">
|
||||
<div class="container">
|
||||
<h1>下载岩美酒库</h1>
|
||||
<p>当前版本 <b class="version-badge">v{{ changelog[0].version }}</b> · 一个账号五端同步 · 客户端内置自动更新</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="detect container">
|
||||
<div class="card">
|
||||
<div class="icbox ic-info" id="detect-icon"><svg class="ic"><use href="#i-ic01"/></svg></div>
|
||||
<div>
|
||||
<div class="ph-eyebrow">
|
||||
<span class="version-badge">v{{ changelog[0].version }}</span>
|
||||
<span>下载岩美</span>
|
||||
</div>
|
||||
<h1 class="ph-title">随时随地,<br/>轻松管理酒库。</h1>
|
||||
<p class="ph-lead">
|
||||
Web 版、Windows / macOS 桌面版与 Android 客户端均已上线,iOS 客户端即将推出。
|
||||
</p>
|
||||
<div class="ph-meta">
|
||||
<div class="ph-meta-item">
|
||||
<div class="ph-meta-label">最新版本</div>
|
||||
<div class="ph-meta-val version-badge">v{{ changelog[0].version }}</div>
|
||||
</div>
|
||||
<div class="ph-meta-item">
|
||||
<div class="ph-meta-label">发布日期</div>
|
||||
<div class="ph-meta-val">{{ changelog[0].date }}</div>
|
||||
</div>
|
||||
<div class="ph-meta-item">
|
||||
<div class="ph-meta-label">可用平台</div>
|
||||
<div class="ph-meta-val">Web / Windows / macOS / Android</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Auto-detect card -->
|
||||
<div class="detect-card">
|
||||
<div class="detect-card-head">
|
||||
<span class="head-left">
|
||||
<i data-lucide="zap" class="icon"></i>
|
||||
已自动识别您的环境
|
||||
</span>
|
||||
<span style="font-family: var(--font-mono); color: var(--fg-subtle);">推荐</span>
|
||||
</div>
|
||||
<div class="detect-card-body">
|
||||
<div class="detect-icon" id="detect-icon">
|
||||
<i data-lucide="globe" class="icon"></i>
|
||||
</div>
|
||||
<div class="detect-info">
|
||||
<h3 id="detect-name">Web 版</h3>
|
||||
<p id="detect-desc">浏览器直接访问,功能完整,无需下载安装。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detect-card-actions">
|
||||
<a href="{{ site.appBaseUrl }}/app/" class="btn btn-primary btn-lg" id="detect-btn">
|
||||
<i data-lucide="arrow-right" class="icon"></i>
|
||||
立即使用 Web 版
|
||||
</a>
|
||||
</div>
|
||||
<b id="detect-name">Web 版</b>
|
||||
<span id="detect-desc">已自动识别你的设备,推荐以下版本。</span>
|
||||
</div>
|
||||
<a class="btn primary" id="detect-btn" href="{{ site.appBaseUrl }}/app/"><span>立即使用</span></a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- ============================== PLATFORM GRID ============================== -->
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2 class="fs-3xl fw-6 text-brand-900 m-0 mb-6" style="letter-spacing: var(--tracking-cn-display);">支持的平台</h2>
|
||||
<p class="fs-md text-muted m-0 mb-8">Web 版、Windows / macOS 桌面版与 Android 客户端现已可用,iOS 正在开发中。</p>
|
||||
|
||||
<div class="dl-grid" id="platform-grid">
|
||||
<!-- Web -->
|
||||
<div class="dl-card" data-platform="web">
|
||||
<div class="dl-card-icon"><i data-lucide="globe" class="icon"></i></div>
|
||||
<h3 class="dl-card-name">Web 版</h3>
|
||||
<p class="dl-card-desc">浏览器直接访问,功能完整,无需安装,支持 PC 与平板。</p>
|
||||
<span class="dl-status available">✓ 已上线</span>
|
||||
<a href="{{ site.appBaseUrl }}/app/" class="dl-card-btn primary">
|
||||
<i data-lucide="arrow-right" class="icon"></i>立即使用
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Windows -->
|
||||
<div class="dl-card" data-platform="windows">
|
||||
<div class="dl-card-icon"><i data-lucide="monitor" class="icon"></i></div>
|
||||
<h3 class="dl-card-name">Windows</h3>
|
||||
<p class="dl-card-desc">原生桌面客户端,支持 Win 10/11(64 位),离线模式与本地打印。</p>
|
||||
<span class="dl-status available">✓ 已支持</span>
|
||||
<a href="#" class="dl-card-btn primary" id="windows-dl-btn">
|
||||
<i data-lucide="download" class="icon"></i>下载 Windows 版
|
||||
</a>
|
||||
<div class="icbox ic-info"><svg class="ic"><use href="#i-ic63"/></svg></div>
|
||||
<h3>Windows</h3>
|
||||
<div class="req">Win 10 / 11(64 位)</div>
|
||||
<div class="dl-status">✓ 安装包</div>
|
||||
<a class="btn primary" id="windows-dl-btn" href="{{ site.appBaseUrl }}/downloads/jiu-windows-x64-setup.exe"><svg class="ic" style="width:15px;height:15px"><use href="#i-download"/></svg>下载</a>
|
||||
</div>
|
||||
|
||||
<!-- macOS -->
|
||||
<div class="dl-card" data-platform="macos">
|
||||
<div class="dl-card-icon"><i data-lucide="laptop" class="icon"></i></div>
|
||||
<h3 class="dl-card-name">macOS</h3>
|
||||
<p class="dl-card-desc">支持 Apple Silicon 与 Intel,解压即用,首次运行右键→打开。</p>
|
||||
<span class="dl-status available">✓ 已支持</span>
|
||||
<a href="#" class="dl-card-btn primary" id="macos-dl-btn">
|
||||
<i data-lucide="download" class="icon"></i>下载 macOS 版
|
||||
</a>
|
||||
<div class="icbox ic-info"><svg class="ic"><use href="#i-ic25"/></svg></div>
|
||||
<h3>macOS</h3>
|
||||
<div class="req">Apple Silicon / Intel</div>
|
||||
<div class="dl-status">✓ 已签名公证</div>
|
||||
<a class="btn primary" id="macos-dl-btn" href="{{ site.appBaseUrl }}/downloads/jiu-macos-x64.zip"><svg class="ic" style="width:15px;height:15px"><use href="#i-download"/></svg>下载</a>
|
||||
</div>
|
||||
|
||||
<!-- iOS -->
|
||||
<div class="dl-card coming-soon" data-platform="ios" id="ios-card">
|
||||
<div class="dl-card-icon"><i data-lucide="smartphone" class="icon"></i></div>
|
||||
<h3 class="dl-card-name">iOS</h3>
|
||||
<p class="dl-card-desc">iPhone 与 iPad 通用,通过 TestFlight 安装。</p>
|
||||
<span class="dl-status soon" id="ios-status">敬请期待</span>
|
||||
<a href="#" class="dl-card-btn disabled" id="ios-dl-btn">
|
||||
<i data-lucide="clock" class="icon"></i><span>即将推出</span>
|
||||
</a>
|
||||
<div class="dl-card" data-platform="web">
|
||||
<div class="icbox ic-ok"><svg class="ic"><use href="#i-ic01"/></svg></div>
|
||||
<h3>Web 版</h3>
|
||||
<div class="req">现代浏览器均可</div>
|
||||
<div class="dl-status">✓ 免安装</div>
|
||||
<a class="btn primary" href="{{ site.appBaseUrl }}/app/">立即使用</a>
|
||||
</div>
|
||||
|
||||
<!-- Android -->
|
||||
<div class="dl-card" data-platform="android">
|
||||
<div class="dl-card-icon"><i data-lucide="tablet-smartphone" class="icon"></i></div>
|
||||
<h3 class="dl-card-name">Android</h3>
|
||||
<p class="dl-card-desc">手机与平板通用,APK 直接安装,支持 Android 6.0 及以上。</p>
|
||||
<span class="dl-status available">✓ 已支持</span>
|
||||
<a href="#" class="dl-card-btn primary" id="android-dl-btn">
|
||||
<i data-lucide="download" class="icon"></i>下载 Android 版
|
||||
</a>
|
||||
<div class="icbox ic-warn"><svg class="ic"><use href="#i-ic43"/></svg></div>
|
||||
<h3>Android</h3>
|
||||
<div class="req">Android 6.0 及以上</div>
|
||||
<div class="dl-status">✓ APK 直装</div>
|
||||
<a class="btn primary" id="android-dl-btn" href="{{ site.appBaseUrl }}/downloads/jiu-android.apk"><svg class="ic" style="width:15px;height:15px"><use href="#i-download"/></svg>下载</a>
|
||||
</div>
|
||||
<div class="dl-card" data-platform="ios" id="ios-card">
|
||||
<div class="icbox ic-wine"><svg class="ic"><use href="#i-ic22"/></svg></div>
|
||||
<h3>iOS</h3>
|
||||
<div class="req">iPhone / iPad</div>
|
||||
<div class="dl-status soon" id="ios-status">TestFlight 内测中</div>
|
||||
<a class="btn ghost disabled" id="ios-dl-btn" href="#">敬请期待</a>
|
||||
</div>
|
||||
</div>
|
||||
<p style="text-align:center;color:var(--faint);font-size:var(--fs-sm);margin-top:18px">
|
||||
桌面与移动客户端内置自动更新 · 各端详细配置见<a href="/system-requirements/">系统要求</a>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============================== SYSTEM REQUIREMENTS ============================== -->
|
||||
<section class="section bg-25">
|
||||
<section class="section alt" id="changelog">
|
||||
<div class="container">
|
||||
<h2 class="fs-3xl fw-6 text-brand-900 m-0 mb-6" style="letter-spacing: var(--tracking-cn-display);">系统要求</h2>
|
||||
<p class="fs-md text-muted m-0 mb-32">在以下环境中均经过测试与验证。</p>
|
||||
<div class="card p-32 sysreq-content">
|
||||
{{ sysreq | safe }}
|
||||
<div class="sec-head">
|
||||
<div class="eyebrow">更新日志</div>
|
||||
<h2>最近更新</h2>
|
||||
<p id="changelog-count">最近 {{ changelog | length }} 个版本。</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============================== CHANGELOG ============================== -->
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2 class="fs-3xl fw-6 text-brand-900 m-0 mb-6" style="letter-spacing: var(--tracking-cn-display);">版本更新日志</h2>
|
||||
<p class="fs-md text-muted m-0" id="changelog-count">最近 {{ changelog | length }} 个版本。</p>
|
||||
|
||||
<div class="changelog" id="changelog-list">
|
||||
{% for entry in changelog %}
|
||||
<div class="changelog-entry{% if loop.first %} current{% endif %}">
|
||||
@@ -401,27 +117,14 @@ pageStyle: |
|
||||
{% if loop.first %}<span class="changelog-tag">当前版本</span>{% endif %}
|
||||
</div>
|
||||
<div class="changelog-body">
|
||||
{% if entry.intro %}
|
||||
<p class="changelog-intro">{{ entry.intro }}</p>
|
||||
{% endif %}
|
||||
{% if entry.intro %}<p class="changelog-intro">{{ entry.intro }}</p>{% endif %}
|
||||
{% for section in entry.sections %}
|
||||
<div class="changelog-cat">
|
||||
<div class="changelog-cat-label">
|
||||
{% if section.type == "新功能" %}
|
||||
<span class="dot dot-feature"></span>新功能
|
||||
{% elif section.type == "改进" %}
|
||||
<span class="dot dot-improve"></span>改进
|
||||
{% elif section.type == "修复" %}
|
||||
<span class="dot dot-fix"></span>问题修复
|
||||
{% else %}
|
||||
<span class="dot dot-improve"></span>{{ section.type }}
|
||||
{% endif %}
|
||||
<span class="dot {% if section.type == '新功能' %}dot-feature{% elif section.type == '修复' %}dot-fix{% else %}dot-improve{% endif %}"></span>
|
||||
{{ section.type }}
|
||||
</div>
|
||||
<ul>
|
||||
{% for item in section.items %}
|
||||
<li>{{ item }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<ul>{% for item in section.items %}<li>{{ item }}</li>{% endfor %}</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -434,12 +137,13 @@ pageStyle: |
|
||||
<script>
|
||||
(function() {
|
||||
var APP_BASE = '{{ site.appBaseUrl }}';
|
||||
var SPRITE = { web:'#i-ic01', ios:'#i-ic22', android:'#i-ic43', windows:'#i-ic63', macos:'#i-ic25' };
|
||||
var platforms = {
|
||||
web: { name: 'Web 版', desc: '浏览器直接访问,功能完整,无需下载安装。', icon: 'globe', btnText: '立即使用 Web 版', btnHref: APP_BASE + '/app/', available: true },
|
||||
ios: { name: 'iOS', desc: 'iPhone 与 iPad 通用,通过 TestFlight 安装。', icon: 'smartphone', btnText: '敬请期待', available: false },
|
||||
android: { name: 'Android', desc: '手机与平板通用,APK 直接安装,支持 Android 6.0 及以上。', icon: 'tablet-smartphone', btnText: '下载 Android 版', btnHref: '#', available: true },
|
||||
windows: { name: 'Windows 桌面', desc: '原生桌面客户端,支持 Win 10/11(64 位),离线模式与本地打印。', icon: 'monitor', btnText: '下载 Windows 版', btnHref: '#', available: true },
|
||||
macos: { name: 'macOS 桌面', desc: '支持 Apple Silicon 与 Intel,解压即用,首次运行右键→打开。', icon: 'laptop', btnText: '下载 macOS 版', btnHref: '#', available: true }
|
||||
web: { name: 'Web 版', desc: '浏览器直接访问,功能完整,无需下载安装。', btnText: '立即使用 Web 版', btnHref: APP_BASE + '/app/', available: true },
|
||||
ios: { name: 'iOS', desc: 'iPhone 与 iPad 通用,通过 TestFlight 安装。', btnText: '敬请期待', available: false },
|
||||
android: { name: 'Android', desc: '手机与平板通用,APK 直接安装。', btnText: '下载 Android 版', btnHref: APP_BASE + '/downloads/jiu-android.apk', available: true },
|
||||
windows: { name: 'Windows 桌面', desc: '原生桌面客户端,支持 Win 10/11(64 位)与本地打印。', btnText: '下载 Windows 版', btnHref: APP_BASE + '/downloads/jiu-windows-x64-setup.exe', available: true },
|
||||
macos: { name: 'macOS 桌面', desc: '支持 Apple Silicon 与 Intel,解压即用。', btnText: '下载 macOS 版', btnHref: APP_BASE + '/downloads/jiu-macos-x64.zip', available: true }
|
||||
};
|
||||
|
||||
function detectPlatform() {
|
||||
@@ -461,27 +165,18 @@ pageStyle: |
|
||||
if (nameEl) nameEl.textContent = info.name;
|
||||
if (descEl) descEl.textContent = info.desc;
|
||||
if (btnEl) {
|
||||
if (info.available) {
|
||||
if (info.available && info.btnHref && info.btnHref !== '#') {
|
||||
btnEl.href = info.btnHref;
|
||||
btnEl.style.opacity = '';
|
||||
btnEl.style.pointerEvents = '';
|
||||
btnEl.querySelector('span') && (btnEl.querySelector('span').textContent = info.btnText);
|
||||
} else {
|
||||
btnEl.classList.remove('disabled');
|
||||
btnEl.innerHTML = '<span>' + info.btnText + '</span>';
|
||||
} else if (!info.available) {
|
||||
btnEl.href = '#';
|
||||
btnEl.style.opacity = '0.5';
|
||||
btnEl.style.pointerEvents = 'none';
|
||||
btnEl.classList.add('disabled');
|
||||
btnEl.innerHTML = '<span>' + info.btnText + '</span>';
|
||||
}
|
||||
btnEl.childNodes.forEach(function(n) {
|
||||
if (n.nodeType === 3) n.textContent = ' ' + info.btnText;
|
||||
});
|
||||
}
|
||||
if (iconEl && window.lucide) {
|
||||
iconEl.innerHTML = '';
|
||||
var svg = document.createElement('i');
|
||||
svg.setAttribute('data-lucide', info.icon);
|
||||
svg.className = 'icon';
|
||||
iconEl.appendChild(svg);
|
||||
lucide.createIcons();
|
||||
if (iconEl) {
|
||||
iconEl.innerHTML = '<svg class="ic"><use href="' + (SPRITE[p] || '#i-ic01') + '"/></svg>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,13 +194,11 @@ pageStyle: |
|
||||
});
|
||||
}
|
||||
|
||||
// 用 /api/v1/public/release 返回的 changelog 重渲染更新日志时间线,使客户端
|
||||
// 发版无需重建官网即可同步。结构与构建时 web/_data/changelog.js 一致;接口
|
||||
// 不可达或字段缺失时保留构建时静态时间线作 fallback。
|
||||
// 用 /api/v1/public/release 的 changelog 重渲染时间线:客户端发版无需重建官网。
|
||||
// 接口不可达或字段缺失时保留构建时静态时间线作 fallback。
|
||||
function renderChangelog(entries) {
|
||||
if (!Array.isArray(entries) || entries.length === 0) return;
|
||||
var dotClass = { '新功能': 'dot-feature', '改进': 'dot-improve', '修复': 'dot-fix' };
|
||||
var label = { '修复': '问题修复' };
|
||||
function esc(s) {
|
||||
return String(s == null ? '' : s).replace(/[&<>"]/g, function(c) {
|
||||
return { '&': '&', '<': '<', '>': '>', '"': '"' }[c];
|
||||
@@ -513,22 +206,16 @@ pageStyle: |
|
||||
}
|
||||
var html = '';
|
||||
entries.forEach(function(entry, i) {
|
||||
var current = i === 0;
|
||||
html += '<div class="changelog-entry' + (current ? ' current' : '') + '">';
|
||||
html += '<div class="changelog-meta">';
|
||||
html += '<div class="changelog-version">v' + esc(entry.version) + '</div>';
|
||||
html += '<div class="changelog-entry' + (i === 0 ? ' current' : '') + '">';
|
||||
html += '<div class="changelog-meta"><div class="changelog-version">v' + esc(entry.version) + '</div>';
|
||||
html += '<div class="changelog-date">' + esc(entry.date) + '</div>';
|
||||
if (current) html += '<span class="changelog-tag">当前版本</span>';
|
||||
if (i === 0) html += '<span class="changelog-tag">当前版本</span>';
|
||||
html += '</div><div class="changelog-body">';
|
||||
if (entry.intro) html += '<p class="changelog-intro">' + esc(entry.intro) + '</p>';
|
||||
(entry.sections || []).forEach(function(section) {
|
||||
var dot = dotClass[section.type] || 'dot-improve';
|
||||
var name = label[section.type] || section.type;
|
||||
html += '<div class="changelog-cat"><div class="changelog-cat-label">';
|
||||
html += '<span class="dot ' + dot + '"></span>' + esc(name) + '</div><ul>';
|
||||
(section.items || []).forEach(function(item) {
|
||||
html += '<li>' + esc(item) + '</li>';
|
||||
});
|
||||
html += '<div class="changelog-cat"><div class="changelog-cat-label"><span class="dot ' + dot + '"></span>' + esc(section.type) + '</div><ul>';
|
||||
(section.items || []).forEach(function(item) { html += '<li>' + esc(item) + '</li>'; });
|
||||
html += '</ul></div>';
|
||||
});
|
||||
html += '</div></div>';
|
||||
@@ -544,63 +231,50 @@ pageStyle: |
|
||||
updateDetectCard(detected);
|
||||
highlightPlatformCard(detected);
|
||||
|
||||
fetch('{{ site.appBaseUrl }}/api/v1/public/release')
|
||||
fetch(APP_BASE + '/api/v1/public/release')
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) {
|
||||
if (data && data.version) updateVersionBadges(data.version);
|
||||
if (data && data.changelog) renderChangelog(data.changelog);
|
||||
if (data && data.download_urls && data.download_urls.macos) {
|
||||
platforms.macos.btnHref = data.download_urls.macos;
|
||||
var macBtn = document.getElementById('macos-dl-btn');
|
||||
if (macBtn) macBtn.href = data.download_urls.macos;
|
||||
if (detected === 'macos') updateDetectCard(detected);
|
||||
}
|
||||
if (data && data.download_urls && data.download_urls.windows) {
|
||||
platforms.windows.btnHref = data.download_urls.windows;
|
||||
var btn = document.getElementById('windows-dl-btn');
|
||||
if (btn) btn.href = data.download_urls.windows;
|
||||
if (detected === 'windows') updateDetectCard(detected);
|
||||
}
|
||||
if (data && data.download_urls && data.download_urls.android) {
|
||||
platforms.android.btnHref = data.download_urls.android;
|
||||
var andBtn = document.getElementById('android-dl-btn');
|
||||
if (andBtn) andBtn.href = data.download_urls.android;
|
||||
if (detected === 'android') updateDetectCard(detected);
|
||||
}
|
||||
// iOS 走 TestFlight:仅当配置了公开 TestFlight 链接时激活卡片
|
||||
if (data && data.download_urls && data.download_urls.ios) {
|
||||
var urls = (data && data.download_urls) || {};
|
||||
[['macos','macos-dl-btn'],['windows','windows-dl-btn'],['android','android-dl-btn']].forEach(function(pair) {
|
||||
if (urls[pair[0]]) {
|
||||
platforms[pair[0]].btnHref = urls[pair[0]];
|
||||
var btn = document.getElementById(pair[1]);
|
||||
if (btn) btn.href = urls[pair[0]];
|
||||
if (detected === pair[0]) updateDetectCard(detected);
|
||||
}
|
||||
});
|
||||
// iOS 走 TestFlight:仅当返回公开链接时激活
|
||||
if (urls.ios) {
|
||||
platforms.ios.available = true;
|
||||
platforms.ios.btnHref = data.download_urls.ios;
|
||||
platforms.ios.btnHref = urls.ios;
|
||||
platforms.ios.btnText = '通过 TestFlight 安装';
|
||||
var iosCard = document.getElementById('ios-card');
|
||||
var iosStatus = document.getElementById('ios-status');
|
||||
var iosBtn = document.getElementById('ios-dl-btn');
|
||||
if (iosCard) iosCard.classList.remove('coming-soon');
|
||||
if (iosStatus) { iosStatus.className = 'dl-status available'; iosStatus.textContent = '✓ 已上线'; }
|
||||
if (iosStatus) { iosStatus.className = 'dl-status'; iosStatus.textContent = '✓ 已上线'; }
|
||||
if (iosBtn) {
|
||||
iosBtn.className = 'dl-card-btn primary';
|
||||
iosBtn.href = data.download_urls.ios;
|
||||
iosBtn.className = 'btn primary';
|
||||
iosBtn.href = urls.ios;
|
||||
iosBtn.target = '_blank';
|
||||
iosBtn.rel = 'noopener';
|
||||
iosBtn.innerHTML = '<i data-lucide="download" class="icon"></i>通过 TestFlight 安装';
|
||||
if (window.lucide) lucide.createIcons();
|
||||
iosBtn.textContent = 'TestFlight 安装';
|
||||
}
|
||||
if (detected === 'ios') updateDetectCard(detected);
|
||||
}
|
||||
})
|
||||
.catch(function() {
|
||||
// API 不可达时,将仍为 href="#" 的下载按钮改为禁用态
|
||||
['windows-dl-btn', 'macos-dl-btn', 'android-dl-btn'].forEach(function(id) {
|
||||
var btn = document.getElementById(id);
|
||||
if (btn && btn.getAttribute('href') === '#') {
|
||||
btn.className = 'dl-card-btn disabled';
|
||||
btn.innerHTML = '暂时无法获取下载地址';
|
||||
btn.className = 'btn ghost disabled';
|
||||
btn.textContent = '暂时无法获取下载地址';
|
||||
}
|
||||
});
|
||||
var detectBtn = document.getElementById('detect-btn');
|
||||
if (detectBtn && detectBtn.getAttribute('href') === '#') {
|
||||
detectBtn.className = 'btn btn-secondary btn-lg';
|
||||
detectBtn.innerHTML = '暂时无法连接服务器';
|
||||
detectBtn.classList.add('disabled');
|
||||
detectBtn.innerHTML = '<span>暂时无法连接服务器</span>';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user