feat(client): 购买卡按 render_type 分发——redirect 外跳/qr 内嵌二维码/重选取消订单

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-07-10 23:46:19 +08:00
parent 179051a8fa
commit 9fe1e2766f
6 changed files with 101 additions and 10 deletions
+17 -2
View File
@@ -52,6 +52,8 @@
.buy .fine{font-size:var(--fs-xs); color:var(--faint); margin-top:6px;}
.buy .acts{display:flex; justify-content:flex-end; gap:10px; margin-top:12px;}
.buy .obox{padding:12px; background:var(--bg); border:1px solid var(--border-subtle); border-radius:var(--r-md); margin-top:14px;}
/* qr 态二维码占位(render_type=qr,pay 侧预留未实现,仅演示两态)—— 待提升至 atoms.css */
.buy .qr-ph{width:180px; height:180px; margin:12px auto 0; border:1px solid var(--border); border-radius:var(--r-md); background:var(--bg); display:flex; align-items:center; justify-content:center; color:var(--faint); font-size:var(--fs-xs); text-align:center; box-sizing:border-box;}
.buy .spin{width:18px; height:18px; border-radius:50%; border:2px solid var(--border); border-top-color:var(--primary); animation:buy-spin .8s linear infinite; flex:none;}
@keyframes buy-spin{to{transform:rotate(360deg)}}
.buy .okic{width:40px; height:40px; border-radius:var(--r-md); background:var(--ok-soft); color:var(--success); display:flex; align-items:center; justify-content:center; flex:none;}
@@ -195,7 +197,10 @@ const PLANS={
standard:{name:'标准版',monthly:299,annual:2999,feats:['单门店 · 单仓库','2 台客户端同时使用','1,000 张商品图片分享']},
pro:{name:'高级版',monthly:599,annual:5999,feats:['单门店 · 多仓库','5 台客户端同时使用','10,000 张商品图片分享','免费 AI 周度 / 月度商业数据分析']},
};
const buy={tab:'promo',cycle:'annual',phase:'pick',promoUsed:false};
// renderTypepay v2 收银台会话形态(redirect|qr)。真机 alipay 目前只返回
// redirectqr 是 pay 侧未实现的预留分支——演示开关仅供原型评审对照两态 UI,
// 非真实产品控件(真实按后端 render_type 自动分发,见 purchase_card.dart)。
const buy={tab:'promo',cycle:'annual',phase:'pick',promoUsed:false,renderType:'redirect'};
function fmt(n){ return n.toLocaleString(); }
function segHtml(items,cur,fn){ return `<div class="seg">${items.map(([k,label])=>`<button class="${cur===k?'on':''}" onclick="${fn}('${k}')">${label}</button>`).join('')}</div>`; }
function buyTab(k){ if(k==='promo'&&buy.promoUsed){ toast('本店已享受过首月特惠'); } buy.tab=k; drawBuy(); }
@@ -208,7 +213,17 @@ function drawBuy(){
const el=document.getElementById('buyCard');
if(buy.phase==='waiting'){
const p=buy.tab==='promo'?PLANS.promo.price:(buy.cycle==='annual'?PLANS[buy.tab].annual:PLANS[buy.tab].monthly);
el.innerHTML=`<div style="display:flex;align-items:center;gap:12px"><div class="spin"></div><div style="flex:1;font-size:var(--fs-sm);color:var(--text)">已在浏览器打开支付宝收银台,完成支付后本卡片会自动确认到账。</div></div>
const isQr=buy.renderType==='qr';
// 演示开关:redirect/qr 两态切换,仅原型评审对照用(详见 buy 对象注释)
const modeSeg=`<div class="seg" style="margin-bottom:12px">
<button class="${!isQr?'on':''}" onclick="buy.renderType='redirect';drawBuy()">redirect 态</button>
<button class="${isQr?'on':''}" onclick="buy.renderType='qr';drawBuy()">qr 态(预留,pay 侧未实现)</button>
</div>`;
const topRow=isQr
? `<div style="display:flex;align-items:center;gap:12px"><div class="spin"></div><div style="flex:1;font-size:var(--fs-sm);color:var(--text)">打开支付宝扫码支付,完成后本卡片自动确认到账。</div></div>
<div class="qr-ph">二维码占位<br>180×180</div>`
: `<div style="display:flex;align-items:center;gap:12px"><div class="spin"></div><div style="flex:1;font-size:var(--fs-sm);color:var(--text)">已在浏览器打开支付宝收银台,完成支付后本卡片会自动确认到账。</div></div>`;
el.innerHTML=modeSeg+topRow+`
<div class="obox"><div style="font-size:var(--fs-xs);color:var(--muted)">订单号</div><div style="font-size:var(--fs-sm);font-family:var(--font-mono);margin-top:4px">JIU20260710160001</div><div style="font-size:var(--fs-sm);color:var(--text);margin-top:8px">金额 ¥${fmt(p)}</div></div>
<div class="fine" style="margin-top:10px">离开本页不影响到账:支付成功后授权自动续期,可稍后回本页刷新查看。</div>
<div class="acts"><div class="btn ghost" onclick="buyBack()">重新选择</div><div class="btn primary" onclick="buyDone()">我已完成支付</div></div>`;
+17 -2
View File
@@ -44,6 +44,8 @@ body{margin:0; font-family:var(--font); color:var(--text); background:var(--page
.buy .fine{font-size:var(--fs-xs); color:var(--faint); margin-top:6px;}
.buy .acts{display:flex; justify-content:flex-end; gap:10px; margin-top:12px;}
.buy .obox{padding:12px; background:var(--bg); border:1px solid var(--border-subtle); border-radius:var(--r-md); margin-top:14px;}
/* qr 态二维码占位(render_type=qr,pay 侧预留未实现,仅演示两态)—— 待提升至 atoms.css */
.buy .qr-ph{width:180px; height:180px; margin:12px auto 0; border:1px solid var(--border); border-radius:var(--r-md); background:var(--bg); display:flex; align-items:center; justify-content:center; color:var(--faint); font-size:var(--fs-xs); text-align:center; box-sizing:border-box;}
.buy .spin{width:18px; height:18px; border-radius:50%; border:2px solid var(--border); border-top-color:var(--primary); animation:buy-spin .8s linear infinite; flex:none;}
@keyframes buy-spin{to{transform:rotate(360deg)}}
.buy .okic{width:40px; height:40px; border-radius:var(--r-md); background:var(--ok-soft); color:var(--success); display:flex; align-items:center; justify-content:center; flex:none;}
@@ -169,7 +171,10 @@ const PLANS={
standard:{name:'标准版',monthly:299,annual:2999,feats:['单门店 · 单仓库','2 台客户端同时使用','1,000 张商品图片分享']},
pro:{name:'高级版',monthly:599,annual:5999,feats:['单门店 · 多仓库','5 台客户端同时使用','10,000 张商品图片分享','免费 AI 周度 / 月度商业数据分析']},
};
const buy={tab:'promo',cycle:'annual',phase:'pick',promoUsed:false};
// renderTypepay v2 收银台会话形态(redirect|qr)。真机 alipay 目前只返回
// redirectqr 是 pay 侧未实现的预留分支——演示开关仅供原型评审对照两态 UI,
// 非真实产品控件(真实按后端 render_type 自动分发,见 purchase_card.dart)。
const buy={tab:'promo',cycle:'annual',phase:'pick',promoUsed:false,renderType:'redirect'};
function fmt(n){ return n.toLocaleString(); }
function segHtml(items,cur,fn){ return `<div class="seg">${items.map(([k,label])=>`<button class="${cur===k?'on':''}" onclick="${fn}('${k}')">${label}</button>`).join('')}</div>`; }
function buyTab(k){ if(k==='promo'&&buy.promoUsed){ mToast('本店已享受过首月特惠'); } buy.tab=k; drawBuy(); }
@@ -182,7 +187,17 @@ function drawBuy(){
const el=document.getElementById('buyCard');
if(buy.phase==='waiting'){
const p=buy.tab==='promo'?PLANS.promo.price:(buy.cycle==='annual'?PLANS[buy.tab].annual:PLANS[buy.tab].monthly);
el.innerHTML=`<div style="display:flex;align-items:center;gap:12px"><div class="spin"></div><div style="flex:1;font-size:var(--fs-sm);color:var(--text)">已在浏览器打开支付宝收银台,完成支付后本卡片会自动确认到账。</div></div>
const isQr=buy.renderType==='qr';
// 演示开关:redirect/qr 两态切换,仅原型评审对照用(详见 buy 对象注释)
const modeSeg=`<div class="seg" style="margin-bottom:12px">
<button class="${!isQr?'on':''}" onclick="buy.renderType='redirect';drawBuy()">redirect 态</button>
<button class="${isQr?'on':''}" onclick="buy.renderType='qr';drawBuy()">qr 态(预留,pay 侧未实现)</button>
</div>`;
const topRow=isQr
? `<div style="display:flex;align-items:center;gap:12px"><div class="spin"></div><div style="flex:1;font-size:var(--fs-sm);color:var(--text)">打开支付宝扫码支付,完成后本卡片自动确认到账。</div></div>
<div class="qr-ph">二维码占位<br>180×180</div>`
: `<div style="display:flex;align-items:center;gap:12px"><div class="spin"></div><div style="flex:1;font-size:var(--fs-sm);color:var(--text)">已在浏览器打开支付宝收银台,完成支付后本卡片会自动确认到账。</div></div>`;
el.innerHTML=modeSeg+topRow+`
<div class="obox"><div style="font-size:var(--fs-xs);color:var(--muted)">订单号</div><div style="font-size:var(--fs-sm);font-family:var(--font-mono);margin-top:4px">JIU20260710160001</div><div style="font-size:var(--fs-sm);color:var(--text);margin-top:8px">金额 ¥${fmt(p)}</div></div>
<div class="fine" style="margin-top:10px">离开本页不影响到账:支付成功后授权自动续期,可稍后回本页刷新查看。</div>
<div class="acts"><div class="btn ghost" onclick="buyBack()">重新选择</div><div class="btn primary" onclick="buyDone()">我已完成支付</div></div>`;