Files
jiu/web/checkout.njk
T
wangjia d256e7ac85 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
2026-07-03 20:40:21 +08:00

148 lines
6.7 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
layout: base.njk
title: 确认订单
description: 选择套餐与计费周期,在线支付开通或续费门店授权。
permalink: /checkout/
---
{# 官网内购买/续费(需登录):套餐+周期联动汇总 → POST /api/v1/license/purchase
拿 pay_url 跳收银台(契约 pay-contract v1.0.0)。版式仿原型 screens/checkout.html。 #}
<div class="container" style="padding:40px 24px 72px">
<div class="crumb"><a href="/#pricing">购买授权</a><span>/</span><span>确认订单</span></div>
<h1 class="co-title">确认订单</h1>
<div class="checkout">
<div>
<div class="co-card">
<h2>选择套餐</h2>
<div class="seg" id="planSeg">
<button class="seg-btn on" data-v="standard">标准版</button>
<button class="seg-btn" data-v="pro">高级版</button>
</div>
<ul class="co-feats" id="planFeats"></ul>
<div class="seg-label">计费周期</div>
<div class="seg" id="cycleSeg">
<button class="seg-btn on" data-v="annual">年付 <span class="seg-tip">省两个月</span></button>
<button class="seg-btn" data-v="monthly">月付</button>
</div>
</div>
<div class="co-card">
<h2>订单内容</h2>
<div class="co-item">
<div class="co-ic"><svg class="ic"><use href="#i-ic07"/></svg></div>
<div class="co-meta"><b id="itName">标准版授权</b><span id="itDur">365 天授权 · 到期时间自动叠加</span></div>
<div class="co-amt" id="itAmt">¥2,999</div>
</div>
</div>
<div class="co-card">
<h2>付款方式</h2>
<div class="co-pay">
<div class="pm on">
<div class="pic" style="background:#1677FF"><svg class="ic"><use href="#i-ic08"/></svg></div>
<div class="nm"><b>支付宝</b><span>电脑扫码 / 手机拉起支付宝,即时到账</span></div>
<div class="radio"></div>
</div>
<div class="pm off">
<div class="pic" style="background:#8A94A6"><svg class="ic"><use href="#i-ic11"/></svg></div>
<div class="nm"><b>对公转账</b><span>需要合同与对公收款请<a href="mailto:{{ site.support.email }}">联系我们</a></span></div>
</div>
</div>
</div>
</div>
<div class="co-card co-summary">
<h2>费用汇总</h2>
<div class="sum-row"><span id="sumName">标准版 · 年付</span><span id="sumBase">¥2,999</span></div>
<div class="sum-row muted"><span>优惠</span><span> ¥0</span></div>
<div class="sum-div"></div>
<div class="sum-total"><span class="lab">应付总额</span><span class="val" id="sumTotal">¥2,999</span></div>
<div class="auth-alert" id="coErr" style="margin-top:12px"></div>
<button class="btn primary lg" id="coBtn" style="width:100%;margin-top:16px">提交订单</button>
<div class="fine">提交后跳转支付宝收银台,支付成功即时续期到当前门店。时长在现有到期时间上叠加,不覆盖。</div>
</div>
</div>
</div>
<script>
(function () {
var API = '{{ site.appBaseUrl }}';
function $(id) { return document.getElementById(id); }
function token() {
var raw = localStorage.getItem('flutter.access_token');
if (!raw) return null;
try { return JSON.parse(raw); } catch (e) { return raw; }
}
function gotoLogin() { location.href = '/login/?next=' + encodeURIComponent('/checkout/' + location.search); }
if (!token()) { gotoLogin(); return; }
/* 套餐目录:价格仅前端展示,实付金额以 pay 套餐表为准(下单响应回传) */
var PLANS = {
standard: {
name: '标准版',
feats: ['单门店 · 单仓库', '2 台客户端同时使用', '1,000 张商品图片分享'],
monthly: { biz: 'monthly_standard', price: 299 },
annual: { biz: 'annual_standard', price: 2999 },
},
pro: {
name: '高级版',
feats: ['单门店 · 多仓库', '5 台客户端同时使用', '10,000 张商品图片分享', '免费 AI 周度 / 月度商业数据分析'],
monthly: { biz: 'monthly_pro', price: 599 },
annual: { biz: 'annual_pro', price: 5999 },
},
};
var q = new URLSearchParams(location.search);
var plan = PLANS[q.get('plan')] ? q.get('plan') : 'standard';
var cycle = 'annual';
function fmt(n) { return '¥' + n.toLocaleString('zh-CN'); }
function render() {
var p = PLANS[plan], c = p[cycle];
var days = cycle === 'annual' ? 365 : 30;
$('planFeats').innerHTML = p.feats.map(function (f) {
return '<li><svg class="ic"><use href="#i-check"/></svg>' + f + '</li>';
}).join('');
$('itName').textContent = p.name + '授权 · ' + (cycle === 'annual' ? '年付' : '月付');
$('itDur').textContent = days + ' 天授权 · 到期时间自动叠加';
$('itAmt').textContent = fmt(c.price);
$('sumName').textContent = p.name + ' · ' + (cycle === 'annual' ? '年付' : '月付');
$('sumBase').textContent = fmt(c.price);
$('sumTotal').textContent = fmt(c.price);
document.querySelectorAll('#planSeg .seg-btn').forEach(function (b) { b.classList.toggle('on', b.dataset.v === plan); });
document.querySelectorAll('#cycleSeg .seg-btn').forEach(function (b) { b.classList.toggle('on', b.dataset.v === cycle); });
}
document.querySelectorAll('#planSeg .seg-btn').forEach(function (b) {
b.addEventListener('click', function () { plan = b.dataset.v; render(); });
});
document.querySelectorAll('#cycleSeg .seg-btn').forEach(function (b) {
b.addEventListener('click', function () { cycle = b.dataset.v; render(); });
});
render();
function showErr(msg) { var el = $('coErr'); el.textContent = msg; el.classList.add('show'); }
$('coBtn').addEventListener('click', function () {
$('coErr').classList.remove('show');
var btn = $('coBtn'); btn.disabled = true; btn.textContent = '正在创建订单…';
fetch(API + '/api/v1/license/purchase', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token() },
body: JSON.stringify({ biz_code: PLANS[plan][cycle].biz }),
})
.then(function (r) {
if (r.status === 401) { gotoLogin(); throw new Error('登录已过期'); }
return r.json().then(function (j) { return { ok: r.ok, j: j }; });
})
.then(function (res) {
if (!res.ok) throw new Error((res.j && res.j.error) || '下单失败,请稍后重试');
var d = res.j.data || {};
if (!d.pay_url) throw new Error('下单响应异常');
location.href = d.pay_url;
})
.catch(function (e) {
showErr(e && e.message ? e.message : '无法连接服务器,请稍后重试');
btn.disabled = false; btn.textContent = '提交订单';
});
});
})();
</script>