feat(backend): 公开页 SSR 化——商品页/店铺页后端直出,顾客动线零 Flutter
- /product/:public_id 从「Flutter 壳注入 OG」改为 go:embed 模板直出轻量页 (~5KB vs 12.5MB 首包,3Mbps 带宽下 30s → <0.5s),OG 分享卡片行为不变 - 新增 /shop/:shop_code 店铺公开页短链:搜索(?q=)+分页(?page=),商品卡互跳 - 数据装配与公开 API 共用(loadPublicProduct / queryShopProducts),口径零漂移 - 图片异步拉取:首图 decoding=async、其余 loading=lazy - 设计:docs/design/public-page-speedup.html Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.PageTitle}}</title>{{.OGTags}}
|
||||
<style>
|
||||
:root{--primary:#2563AC;--primary-dark:#154072;--ink:#232934;--muted:#6E7888;
|
||||
--border:#DCE2EB;--paper:#F5F7FA;--head:#F0F4FF;--ok:#2E8B57;--ok-bg:#E6F3EC;}
|
||||
*{box-sizing:border-box;margin:0;font-family:-apple-system,"PingFang SC","Microsoft YaHei",sans-serif;}
|
||||
body{background:var(--paper);color:var(--ink);line-height:1.65;font-size:15px;}
|
||||
.wrap{max-width:640px;margin:0 auto;padding:14px 14px 40px;}
|
||||
.card{background:#fff;border:1px solid var(--border);border-radius:12px;padding:16px;margin-bottom:12px;}
|
||||
.gallery{display:flex;gap:8px;overflow-x:auto;scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;border-radius:12px;margin-bottom:12px;}
|
||||
.gallery img{width:100%;max-width:640px;flex:0 0 100%;scroll-snap-align:center;border-radius:12px;object-fit:cover;background:#fff;border:1px solid var(--border);}
|
||||
h1{font-size:20px;line-height:1.4;margin-bottom:6px;}
|
||||
.chips{display:flex;flex-wrap:wrap;gap:6px;margin:8px 0 2px;}
|
||||
.chip{font-size:12px;padding:2px 10px;border-radius:10px;background:var(--head);color:var(--primary-dark);}
|
||||
.price{font-size:22px;font-weight:700;color:var(--primary);margin-top:6px;}
|
||||
.price small{font-size:12px;font-weight:400;color:var(--muted);}
|
||||
.row{display:flex;justify-content:space-between;gap:12px;padding:9px 0;border-bottom:1px solid #EEF1F5;font-size:14px;}
|
||||
.row:last-child{border-bottom:none;}
|
||||
.row span{color:var(--muted);flex:none;}
|
||||
.row b{font-weight:600;text-align:right;word-break:break-all;}
|
||||
.sec-t{font-size:13px;font-weight:600;color:var(--primary-dark);margin-bottom:8px;}
|
||||
.desc{font-size:14px;color:var(--ink);white-space:pre-wrap;}
|
||||
.verify{display:flex;align-items:center;gap:8px;background:var(--ok-bg);color:var(--ok);border-radius:10px;padding:10px 14px;font-size:13px;font-weight:600;margin-bottom:12px;}
|
||||
a{color:var(--primary);text-decoration:none;}
|
||||
.shop-name{font-size:16px;font-weight:700;}
|
||||
.foot{text-align:center;color:var(--muted);font-size:12px;margin-top:18px;}
|
||||
.foot a{color:var(--muted);text-decoration:underline;}
|
||||
.empty{text-align:center;padding:60px 20px;color:var(--muted);}
|
||||
.empty h1{font-size:18px;color:var(--ink);margin-bottom:8px;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
{{if .Found}}
|
||||
{{if .Images}}
|
||||
<div class="gallery">
|
||||
{{range $i, $img := .Images}}
|
||||
<img src="{{$img}}" alt="{{$.Name}}" decoding="async"{{if $i}} loading="lazy"{{end}}>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div class="card">
|
||||
<h1>{{.Name}}</h1>
|
||||
{{if .Keywords}}<div class="chips">{{range .Keywords}}<span class="chip">{{.}}</span>{{end}}</div>{{end}}
|
||||
{{if .Price}}<div class="price">{{.Price}}{{if .Unit}} <small>/ {{.Unit}}</small>{{end}}</div>{{end}}
|
||||
</div>
|
||||
|
||||
<div class="verify">✓ 正品保障 · 扫码验真 · 官方渠道供应</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="sec-t">商品信息</div>
|
||||
{{if .Code}}<div class="row"><span>商品编号</span><b>{{.Code}}</b></div>{{end}}
|
||||
{{if .Series}}<div class="row"><span>系列</span><b>{{.Series}}</b></div>{{end}}
|
||||
{{if .Spec}}<div class="row"><span>规格</span><b>{{.Spec}}</b></div>{{end}}
|
||||
{{if .Origin}}<div class="row"><span>产地</span><b>{{.Origin}}</b></div>{{end}}
|
||||
<div class="row"><span>保质期</span><b>{{.ShelfLife}}</b></div>
|
||||
<div class="row"><span>储存方式</span><b>{{.Storage}}</b></div>
|
||||
</div>
|
||||
|
||||
{{if .HasBatch}}
|
||||
<div class="card">
|
||||
<div class="sec-t">批次信息</div>
|
||||
{{if .ProdDate}}<div class="row"><span>生产日期</span><b>{{.ProdDate}}</b></div>{{end}}
|
||||
{{if .BatchNo}}<div class="row"><span>批次号</span><b>{{.BatchNo}}</b></div>{{end}}
|
||||
{{if .InDate}}<div class="row"><span>入库日期</span><b>{{.InDate}}</b></div>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if .DescBody}}
|
||||
<div class="card">
|
||||
<div class="sec-t">{{.DescTitle}}</div>
|
||||
<div class="desc">{{.DescBody}}</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if .HasShop}}
|
||||
<div class="card">
|
||||
<div class="sec-t">门店信息</div>
|
||||
<div class="row"><span>门店</span><b>{{if .ShopCode}}<a class="shop-name" href="/shop/{{.ShopCode}}">{{.ShopName}} ›</a>{{else}}{{.ShopName}}{{end}}</b></div>
|
||||
{{if .ShopPhone}}<div class="row"><span>电话</span><b><a href="tel:{{.ShopPhone}}">{{.ShopPhone}}</a></b></div>{{end}}
|
||||
{{if .ShopAddr}}<div class="row"><span>地址</span><b>{{.ShopAddr}}</b></div>{{end}}
|
||||
{{if .ShopHours}}<div class="row"><span>营业时间</span><b>{{.ShopHours}}</b></div>{{end}}
|
||||
{{if .ShopWx}}<div class="row"><span>微信</span><b>{{.ShopWx}}</b></div>{{end}}
|
||||
{{if .ShopCode}}<div class="row"><span></span><b><a href="/shop/{{.ShopCode}}">查看本店更多商品 ›</a></b></div>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<div class="card empty">
|
||||
<h1>商品不存在或已下架</h1>
|
||||
<p>请确认链接来源,或联系门店核实。</p>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="foot">岩美酒库 · 正品溯源{{if .AppURL}} · <a href="{{.AppURL}}">在 App 中查看</a>{{end}}</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,87 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.PageTitle}}</title>{{.OGTags}}
|
||||
<style>
|
||||
:root{--primary:#2563AC;--primary-dark:#154072;--ink:#232934;--muted:#6E7888;
|
||||
--border:#DCE2EB;--paper:#F5F7FA;--head:#F0F4FF;}
|
||||
*{box-sizing:border-box;margin:0;font-family:-apple-system,"PingFang SC","Microsoft YaHei",sans-serif;}
|
||||
body{background:var(--paper);color:var(--ink);line-height:1.6;font-size:15px;}
|
||||
.wrap{max-width:640px;margin:0 auto;padding:14px 14px 40px;}
|
||||
.head{display:flex;align-items:center;gap:12px;padding:6px 2px 14px;}
|
||||
.head img{width:44px;height:44px;border-radius:10px;object-fit:cover;border:1px solid var(--border);background:#fff;}
|
||||
.head h1{font-size:18px;line-height:1.3;}
|
||||
.head .sub{font-size:12px;color:var(--muted);}
|
||||
form{display:flex;gap:8px;margin-bottom:12px;}
|
||||
input[type=search]{flex:1;border:1px solid var(--border);border-radius:10px;padding:9px 12px;font-size:14px;background:#fff;outline:none;}
|
||||
input[type=search]:focus{border-color:var(--primary);}
|
||||
button{border:none;background:var(--primary);color:#fff;border-radius:10px;padding:0 18px;font-size:14px;}
|
||||
.item{display:flex;gap:12px;background:#fff;border:1px solid var(--border);border-radius:12px;padding:12px;margin-bottom:10px;color:var(--ink);}
|
||||
.item img{width:64px;height:64px;border-radius:8px;object-fit:cover;border:1px solid var(--border);flex:none;background:var(--paper);}
|
||||
.item .ph{width:64px;height:64px;border-radius:8px;border:1px solid var(--border);flex:none;background:var(--head);display:flex;align-items:center;justify-content:center;color:var(--muted);font-size:11px;}
|
||||
.item .m{flex:1;min-width:0;}
|
||||
.item .nm{font-weight:600;font-size:15px;line-height:1.4;}
|
||||
.item .sb{font-size:12.5px;color:var(--muted);margin-top:2px;}
|
||||
.item .cd{font-size:12px;color:var(--muted);font-family:ui-monospace,Menlo,monospace;margin-top:2px;}
|
||||
.item .pr{font-size:15px;font-weight:700;color:var(--primary);flex:none;align-self:center;}
|
||||
a{text-decoration:none;color:inherit;}
|
||||
.pager{display:flex;justify-content:space-between;align-items:center;margin-top:16px;font-size:14px;}
|
||||
.pager a{color:var(--primary);font-weight:600;padding:8px 4px;}
|
||||
.pager .off{color:var(--border);}
|
||||
.pager .pg{color:var(--muted);font-size:12.5px;}
|
||||
.empty{text-align:center;padding:60px 20px;color:var(--muted);background:#fff;border:1px solid var(--border);border-radius:12px;}
|
||||
.empty h2{font-size:16px;color:var(--ink);margin-bottom:6px;}
|
||||
.foot{text-align:center;color:var(--muted);font-size:12px;margin-top:18px;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
{{if .Found}}
|
||||
<div class="head">
|
||||
{{if .LogoURL}}<img src="{{.LogoURL}}" alt="{{.ShopName}}" decoding="async">{{end}}
|
||||
<div>
|
||||
<h1>{{.ShopName}}</h1>
|
||||
<div class="sub">在售商品 {{.Total}} 件 · 正品保障</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="get" action="">
|
||||
<input type="search" name="q" value="{{.Q}}" placeholder="搜索本店商品(名称 / 编号 / 拼音)">
|
||||
<button type="submit">搜索</button>
|
||||
</form>
|
||||
|
||||
{{if .Items}}
|
||||
{{range .Items}}
|
||||
<a class="item" href="/product/{{.PublicID}}">
|
||||
{{if .Img}}<img src="{{.Img}}" alt="{{.Name}}" loading="lazy" decoding="async">{{else}}<span class="ph">暂无图</span>{{end}}
|
||||
<span class="m">
|
||||
<span class="nm">{{.Name}}</span>
|
||||
{{if .Sub}}<span class="sb" style="display:block">{{.Sub}}</span>{{end}}
|
||||
{{if .Code}}<span class="cd" style="display:block">{{.Code}}</span>{{end}}
|
||||
</span>
|
||||
{{if .Price}}<span class="pr">{{.Price}}</span>{{end}}
|
||||
</a>
|
||||
{{end}}
|
||||
<div class="pager">
|
||||
{{if .PrevURL}}<a href="{{.PrevURL}}">‹ 上一页</a>{{else}}<span class="off">‹ 上一页</span>{{end}}
|
||||
<span class="pg">第 {{.Page}} 页 · 共 {{.Total}} 件</span>
|
||||
{{if .NextURL}}<a href="{{.NextURL}}">下一页 ›</a>{{else}}<span class="off">下一页 ›</span>{{end}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="empty">
|
||||
<h2>{{if .Q}}没有找到「{{.Q}}」相关商品{{else}}本店暂无在售商品{{end}}</h2>
|
||||
{{if .Q}}<p>换个关键词试试,或清空搜索查看全部。</p>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<div class="empty">
|
||||
<h2>店铺不存在</h2>
|
||||
<p>请确认链接来源。</p>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="foot">岩美酒库 · 正品溯源</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user