{{include "header.html"}} {{include "nav.html"}} {{$cur := default (index .ThemesExt "currency_symbol") "$"}} {{if index .ThemesExt "show_breadcrumb"}} {{if .Nav}} {{end}} {{end}} {{if .Content}}
{{if .Content.Img}}
{{.Content.Title}}
{{end}}

{{.Content.Title}}

{{/* 评分紧贴标题下方(价格上方):对标里 PDP 评分放这个位置平均提升加购 11% */}} {{if .Content.Ext}} {{$rating := index .Content.Ext "rating"}} {{$reviews := index .Content.Ext "reviews"}} {{if $rating}}
{{$rating}} {{if $reviews}}{{$reviews}} reviews{{end}}
{{end}} {{end}} {{if .Content.Price}}
{{/* ⚠️ id 挂在价格本身那个 div 上,说明文字留在外面:SPU 切换变体时 JS 只改 #vv-price 的 textContent,把说明包进去会被一起抹掉。 */}}
{{$cur}}{{printf "%.2f" .Content.Price}}
{{with index $.ThemesExt "price_note"}}

{{.}}

{{end}}
{{end}} {{if .Content.Ext}} {{/* 两段属性表:**产品属性**(版型/面料/产地)与**贸易条款**(起订量/交期/付款)。 ⚠️ 尺码与颜色的**选择**交给下面的 SPU 复合选择器(切的是真实商品 id, 会进订单);这里只做「说明性」展示,两者不冲突也不重复。 ⚠️ `spec` 那段自由文本保留:运营常用它写一句卖点,删了会让老数据 (default 主题迁过来的商品)丢内容。 ⚠️ 每个字段都独立判空 —— 外贸站的商品常常只填了其中几项, 整表按 `or` 判空、单行按字段判空,才不会出现「有标题没内容」的空表。 */}} {{$spec := index .Content.Ext "spec"}} {{if $spec}}

{{$spec}}

{{end}} {{$size := index .Content.Ext "size"}} {{$color := index .Content.Ext "color"}} {{$material := index .Content.Ext "material"}} {{$fit := index .Content.Ext "fit"}} {{$care := index .Content.Ext "care"}} {{$origin := index .Content.Ext "origin"}} {{$gsm := index .Content.Ext "gsm"}} {{$shipping := index .Content.Ext "shipping"}} {{if or $size $color $material $fit $care $origin $gsm $shipping}}

Product details

{{if $size}}
Sizes {{$size}}
{{end}} {{if $color}}
Colour {{$color}}
{{end}} {{if $material}}
Material {{$material}}
{{end}} {{if $gsm}}
Fabric weight {{$gsm}}
{{end}} {{if $fit}}
Fit {{$fit}}
{{end}} {{if $care}}
Care {{$care}}
{{end}} {{if $origin}}
Made in {{$origin}}
{{end}} {{if $shipping}}
Ships {{$shipping}}
{{end}}
{{/* 尺码指南:外贸站的刚需入口,链接走主题数据,没有就不显示 */}} {{with index $.ThemesExt "size_guide_url"}} Size guide → {{end}}
{{end}} {{/* 贸易条款表。 外贸买家在询价前最先看的四件事:起订量、交期、付款方式、能不能贴牌。 这块信息缺一个,买家就会跳去问客服(或干脆跳去下一家), 所以刻意放在购买区**上方**、价格与属性之后。 */}} {{$moq := index .Content.Ext "moq"}} {{$leadTime := index .Content.Ext "lead_time"}} {{$sampleTime := index .Content.Ext "sample_time"}} {{$packing := index .Content.Ext "packing"}} {{$port := index .Content.Ext "port"}} {{$incoterm := index .Content.Ext "incoterm"}} {{$payment := index .Content.Ext "payment"}} {{$cert := index .Content.Ext "cert"}} {{$oem := index .Content.Ext "oem"}} {{if or $moq $leadTime $sampleTime $packing $port $incoterm $payment $cert $oem}}

Trade terms

{{if $moq}}
MOQ {{$moq}}
{{end}} {{if $leadTime}}
Lead time {{$leadTime}}
{{end}} {{if $sampleTime}}
Sampling {{$sampleTime}}
{{end}} {{if $packing}}
Packing {{$packing}}
{{end}} {{if $port}}
Port {{$port}}
{{end}} {{if $incoterm}}
Incoterms {{$incoterm}}
{{end}} {{if $payment}}
Payment {{$payment}}
{{end}} {{if $cert}}
Certification {{$cert}}
{{end}} {{if $oem}}
OEM / ODM {{$oem}}
{{end}}
{{/* 条款脚注:默认报价口径与可谈空间,读主题数据,没有就不显示 */}} {{with index $.ThemesExt "trade_terms_note"}}

{{.}}

{{end}}
{{end}} {{end}}

{{.Content.Description}}

{{/* SPU 复合选择器(京东式,2026-10-08)。 spu 字段只做 **id 关联**(索引查询起点);规格维度 = 栏目字段模型里 indexed 的 ext 属性字段,可选值 = 组内商品在这些字段上的取值**并集**。 点击属性组合 → 前端匹配出对应商品 id → **就地**切换标题/相册/价格/库存/ 加购 id,页面不跳转。全量变体数据通过 JSON 数据岛一次性下发。 服务端先渲染初始按钮态(当前商品的属性值高亮),JS 接管后续交互。 🚨 数据岛必须直接 {{.}}(结构体):html/template 在 script 上下文 会把结构体序列化成 JSON 对象字面量;若包一层 Json 函数(返回字符串), 字符串会被再包一层引号并转义,JSON.parse 出来是 string 不是对象。 */}} {{with .Content.SpuPicker}}
{{range .Dimensions}}
{{if .Label}}{{.Label}}{{else}}{{.Key}}{{end}} {{/* 尺码维度旁放「尺码指南」入口;颜色维度不需要 */}} {{if eq .Key "size"}}{{end}}
{{if eq .Key "color"}} {{/* 服装站颜色用色块比文字直观:圆点按颜色名映射填充,名字作无障碍标签 */}} {{range .Values}} {{end}} {{else}} {{range .Values}} {{end}} {{end}}
{{end}}
{{/* 尺码指南:服装站刚需。静态通用表,仅在有 size 维度时由上方按钮切换显示。 */}} {{end}} {{/* 购买区:**询盘为主、在线下单兜底**。 ⚠️ 外贸出口站的真实成交路径是「询价 → 打样 → 下单」,直接加购只适合 小批量试单。所以主按钮是 Request a quote(把商品 id 带到联系页预填), 加购保留但降为次要样式 —— 两个按钮同等权重,买家不知道该点哪个, 结果是两个都不点。 ⚠️ 售罄判定必须用 Content.IsSoldOut —— 它内部看的是 StockLimit 标记位, 不能写成「Stock == 0 即不限量」:限量商品卖到 0 之后那样判会显示成 「不限量」,用户点进去却买不了。 🚨 商城开关三态不能混: · 商城关闭 → 只留询盘,不给加购入口(/cart 此时本身是 404, 渲染出加购按钮等于「按钮在、点下去 404」,比不显示更糟); · 商城开着但售罄 → 仍显示售罄(这是商品状态,与商城开关无关); · 商城开着且有货 → 询盘主按钮 + 加购次要按钮。 顺序上先判商城关闭、后判售罄:两者同时成立时,「商城没开」对用户更有意义。 */}} {{$rfqLabel := default (index $.ThemesExt "rfq_btn") "Request a quote"}} {{if not .ShopEnabled}} {{/* 商城关闭:页面仍可作为商品介绍展示,转化路径只剩询盘。 */}}
{{$rfqLabel}} {{with index $.ThemesExt "whatsapp_link"}} WhatsApp {{end}}
{{else}}
{{/* ---------------- 主路径:询盘 ---------------- */}}
{{$rfqLabel}} {{with index $.ThemesExt "whatsapp_link"}} WhatsApp {{end}}
{{/* ---------------- 兜底:小批量在线下单 ---------------- */}} {{if .Content.IsSoldOut}}
Temporarily out of stock — ask us about the next production run
{{else}}
Ordering a small quantity?
{{/* 限量商品的 max 用 Stock,不限量不给 max(HTML 上限变成无限制)。 条件写「StockLimit != 2」而不是「== 1」:模型默认值是 1, 而老数据行(迁移前建的)该列可能为 0,按 ==1 判断会让它们 意外走进限量分支。 */}}
{{/* 「View cart(N)」。 🚨 N 用 span 包起来并给 id:它是服务端渲染的快照,加购后必须由 js.html 的 VMCartBadge 同步 —— 同一个页面上头栏角标变了、 这个数字没变,用户会以为加购没生效。 */}} View cart({{if .Cart}}{{.Cart.TotalQty}}{{end}})

{{if ne .Content.StockLimit 2}}{{.Content.Stock}} in stock{{else}}In stock{{end}}

{{end}} {{/* 购买区信任集群:CTA 附近 3 条图标化承诺横排,比整段文字更能促决策。 🚨 读 service_promises 的前 3 条而不是写死文案:与首页、页脚共用同一份 主题数据,运营在后台改一次全站生效。写死曾让这套英文主题的购买区 冒出中文承诺,且后台改不动。 */}} {{$pdpPromises := index $.ThemesExt "service_promises"}} {{if $pdpPromises}}
    {{range $i, $p := $pdpPromises}}{{if lt $i 3}}
  • {{index $p "title"}}
  • {{end}}{{end}}
{{end}} {{/* 加购反馈区:ID 被页尾那段加购 JS 监听,售罄时不存在也无妨。 */}}

{{end}}

Product details

{{.Content.Content|Str2Html}}
{{$albums := .Content.Albums}} {{if and $albums (ne (index $albums 0) "[]")}}

Gallery

{{range $albums}} {{if and . (ne . "[]")}}
{{$.Content.Title}} photo
{{end}} {{end}}
{{end}} {{if .Content.Tags}}

Tags

{{range .Content.Tags}} {{.Name}} {{end}}
{{end}}

You may also like

{{$related := Related .Content.Id 4}} {{if $related}}
{{range $related}}
{{if .Img}}
{{.Title}}
{{end}}
{{end}}
{{end}}
{{end}} {{include "footer.html"}} {{include "js.html"}} {{if and .Content .Content.SpuPicker}} {{/* 数据岛:全量变体一次性下发。🚨 必须直接输出结构体 —— 包一层 Json 函数会让 JSON.parse 出 string。 */}} {{end}} {{/* vv-pdp-deepened */}}