Files
cms-SiteForge/themes/trade-pro/pages/product.html
T
浅念 257c89e159 fix: product inquiry honeypot hidden + review minors (10b quality round)
- .pro-honeypot hidden under .sf-theme-trade-pro scope (was visible:
  pro-base hide rule needs .pro-inquiry ancestor, new form lacks it)
- drop redundant .sf-tp-photo class on placeholder branch (dual
  aspect-ratio, shadow asymmetry)
- spec table filters 'gallery' rows (string-form gallery leaked as a row)
- comment documents pro.js sibling-node constraint on gallery markup

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-23 11:43:52 +08:00

146 lines
7.9 KiB
HTML
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.
{# trade-pro product detail — cms 产品详情设计移植(Task 10b)。
区块序列对照 D:/project/cms/runtimes/astro-site-runtime/src/pages/[locale]/products/[slug].astro
(像素基准 dist/hesupipe/en/products/alloy-resin-system-window/):
面包屑 → 画廊(主图 + 单行横滚缩略图)+ 标题区(眉题/h1/导语/品牌行/
CTA 对)→ 正文 Product Details → 参数表 Specifications(fields 键值,
空则整段隐藏)→ 相关产品(卡片网格)→ 询盘板块(左联系信息 + 右 CTA/
外部表单)。
与 cms 的两处结构决策:
1. Tab 导航平铺:cms 的 Tab 序列承载每产品自定义区块,平台正文是单一
富文本 + 参数表,仅两个面板不值得引入 JS Tab;cms 无 JS 回退形态
本就是面板平铺,这里固定为平铺(无 JS 依赖、静态可抓取)。
2. 画廊交互复用平台 pro.js 既有钩子(.pro-product-media /
.pro-gallery-thumbs / [data-gallery-image]):缩略图点击换主图 +
主图灯箱,零新增 JS;无 JS 时全部图片仍平铺可读。无图回退封面,
再无则占位渐变(空值不破版)。 #}
<section class="sf-tp-product-hero">
<div class="wrap">
<nav class="sf-tp-breadcrumb" aria-label="Breadcrumb" data-reveal>
<a href="/">Home</a><a href="/products/">Products</a><span class="sf-tp-breadcrumb-current">{{ page.title }}</span>
</nav>
<div class="sf-tp-product-grid">
{# pro.js 经 .pro-gallery-thumbs 的 parentElement 定位 .pro-product-media img——
缩略图容器必须保持为 .pro-product-media 的兄弟节点(不可外包滚动容器) #}
<div class="sf-tp-product-gallery" data-reveal>
{% if gallery %}
<div class="sf-tp-product-media pro-product-media">
<img class="sf-tp-product-main-img" src="{{ gallery[0] }}" alt="{{ page.title }}" width="800" height="600" loading="eager" decoding="async">
</div>
{% if gallery|length > 1 %}
<div class="sf-tp-product-thumbs pro-gallery-thumbs" aria-label="Product image gallery">
{% for photo in gallery %}
<button type="button" class="sf-tp-product-thumb" data-gallery-image="{{ photo }}" aria-label="View image {{ loop.index }}" aria-pressed="{{ 'true' if loop.first else 'false' }}"><img src="{{ photo }}" alt="{{ page.title }} image {{ loop.index }}" width="150" height="100" loading="lazy" decoding="async"></button>
{% endfor %}
</div>
{% endif %}
{% else %}
<div class="sf-tp-product-media pro-product-media">
<div class="sf-tp-photo-plain" aria-hidden="true"><span>PRODUCT</span></div>
</div>
{% endif %}
</div>
<div class="sf-tp-product-summary" data-reveal style="--reveal-i: 1">
<span class="eyebrow">PRODUCT DETAILS / {{ fields.get('brand') or 'CATALOGUE' }}</span>
<h1 class="sf-tp-product-title">{{ page.title }}</h1>
<p class="sf-tp-product-lead">{{ page.summary or 'Detailed product information, specifications and export packaging are published on this page.' }}</p>
{% if fields.get('brand') or fields.get('model') %}
<p class="sf-tp-product-meta">{% if fields.get('brand') %}<span>Brand · {{ fields.get('brand') }}</span>{% endif %}{% if fields.get('model') %}<span>Model · {{ fields.get('model') }}</span>{% endif %}</p>
{% endif %}
<div class="sf-tp-product-actions">
<a class="sf-tp-btn" href="/contact/">Get a quote {{ icon('arrow-right') }}</a>
<a class="sf-tp-btn sf-tp-btn--ghost" href="/products/">All products {{ icon('arrow-right') }}</a>
</div>
</div>
</div>
</div>
</section>
<section class="section sf-tp-product-overview">
<div class="wrap">
<span class="eyebrow">OVERVIEW</span>
<h2 class="sf-tp-panel-title">Product details</h2>
<div class="sf-tp-product-body" data-reveal>{{ body_copy(content, page) }}</div>
</div>
</section>
{% if specs %}
<section class="section section--soft sf-tp-product-specs">
<div class="wrap">
<span class="eyebrow">SPECIFICATIONS</span>
<h2 class="sf-tp-panel-title">Specifications</h2>
<div class="sf-tp-table-scroll" data-reveal>
<table class="sf-tp-spec-table">
<tbody>
{# theme_pro excludes management fields; 'gallery' is a media list, not a spec #}
{% for row in specs if row.label != 'gallery' %}
<tr><th scope="row">{{ row.label }}</th><td>{{ row.value }}</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</section>
{% endif %}
{% set tp_related = (groups['product'] + groups['vehicle']) | rejectattr('route', 'equalto', page.route) | list %}
{% if tp_related %}
<section class="section">
<div class="wrap">
<div class="sf-tp-section-head" data-reveal>
<div>
<span class="eyebrow">RELATED PRODUCTS</span>
<h2>You may also need</h2>
</div>
<a class="sf-tp-more" href="/products/">Browse all products {{ icon('arrow-right', cls='sf-tp-icon--sm') }}</a>
</div>
<div class="sf-tp-card-grid">
{% for tp_item in tp_related[:4] %}
<article class="sf-tp-card" data-reveal style="--reveal-i: {{ loop.index0 % 4 }}">
<div class="sf-tp-card-media">{{ cover(tp_item, 'product') }}</div>
<div class="sf-tp-card-body">
<h3>{{ tp_item.title }}</h3>
<p>{{ (tp_item.summary or 'Published specifications available on request.').split('.')[0] }}.</p>
<a class="sf-tp-more" href="{{ safe_href(tp_item.route) }}">View details {{ icon('arrow-right', cls='sf-tp-icon--sm') }}</a>
</div>
</article>
{% endfor %}
</div>
</div>
</section>
{% endif %}
<section class="section section--soft sf-tp-product-quote">
<div class="wrap sf-tp-quote-grid">
<div class="sf-tp-quote-info" data-reveal>
<span class="eyebrow">REQUEST A QUOTE</span>
<h2>Inquire about this product</h2>
<p>Send the specifications you need for <strong>{{ page.title }}</strong> and our trade desk replies with pricing, lead time and export packaging within one business day.</p>
<div class="sf-tp-quote-list">
{% if footer_email %}
<div class="sf-tp-quote-item">
<div class="icon-circle" aria-hidden="true">{{ icon('mail') }}</div>
<div><strong>Email</strong><p><a href="mailto:{{ footer_email }}">{{ footer_email }}</a></p></div>
</div>
{% endif %}
<div class="sf-tp-quote-item">
<div class="icon-circle" aria-hidden="true">{{ icon('check') }}</div>
<div><strong>Direct sourcing desk</strong><p>One contact from inquiry to container loading.</p></div>
</div>
</div>
</div>
<div class="sf-tp-quote-form" data-reveal style="--reveal-i: 1">
{% if form_endpoint %}
<h3>Get a quote for {{ page.title }}</h3>
<form action="{{ form_endpoint }}" method="POST">
<label>Name <input name="name" autocomplete="name" required maxlength="100"></label>
<label>Email <input name="email" type="email" autocomplete="email" required maxlength="180"></label>
<label>Message <textarea name="message" required minlength="6" maxlength="5000" placeholder="I would like a quotation for {{ page.title }}..."></textarea></label>
<input name="website" tabindex="-1" autocomplete="off" class="pro-honeypot" aria-hidden="true">
<button type="submit" class="sf-tp-btn">Submit enquiry {{ icon('arrow-right') }}</button>
<p class="sf-tp-quote-note">Submissions are handled by the external form service configured by the site administrator.</p>
</form>
{% else %}
<h3>Get a quote for {{ page.title }}</h3>
<p>Share your specifications and target market through the contact page; our trade desk replies within one business day.</p>
<a class="sf-tp-btn" href="/contact/">Contact us {{ icon('arrow-right') }}</a>
{% endif %}
</div>
</div>
</section>