0ec826bd4b
Task 10g: rewrite pages/case.html from the thin 1-line variant to the cms projects/[slug].astro design (pixel baseline dist/hesupipe/en/projects/): PageHero (breadcrumb Home > Cases > title, cover bg) + 1.2fr/0.8fr detail grid (overview block with location/industry rows + rich body via the 10d typography layer; sticky side column with main photo, 3-col gallery tiles and Results card) + Recommended Products + Other Projects (groups['case'] minus self), all empty-data sections hidden. Results points come from the open fields.results convention (semicolon/newline separated); cms dictionary fallback copy is never ported (fake data across sites). Listing case branch (10c evaluation): core form (hero + cols-3 cards + pagination) already matches cms projects list; only the hero copy voice differed, so listing.html gains a case-aware eyebrow/lead fallback (OUR PROJECTS). cms-specific tabs (no data source) and the FEATURED CASE STUDY band (HESU-static dictionary content) stay unported, documented in the template header. Adds test_trade_pro_case_layout_structure (build-flow: gallery/results structure, related-case dedup, empty-data hiding, /cases/ hero voice). Co-Authored-By: Claude Code <noreply@anthropic.com>
128 lines
6.9 KiB
HTML
128 lines
6.9 KiB
HTML
{# trade-pro case detail — cms 案例详情设计移植(Task 10g)。
|
||
区块序列对照 D:/project/cms/runtimes/astro-site-runtime/src/pages/[locale]/projects/[slug].astro
|
||
(像素基准 dist/hesupipe/en/projects/<slug>/):
|
||
PageHero(面包屑 Home › Cases › 标题 + 封面底图 + 眉题/h1/导语)→
|
||
detail-grid 双栏(1.2fr 文案列:Project Overview 眉题 + h2 + ● location/
|
||
industry 行 + 富文本正文;0.8fr sticky 侧列:主图 + 3 列相册缩略图 +
|
||
Results 成果卡)→ Recommended Products(soft 底产品卡)→ Other Projects
|
||
(相关案例卡,排除自身)。
|
||
与 cms 的结构决策:
|
||
1. cms 文案列是 customerBackground / challenge / solution 三个结构化字段
|
||
(空时回退 HESU 词典段落);平台案例只有单一富文本 body——概览块后
|
||
正文一整块渲染(10d sf-tp-article-body 排版层双挂),Challenge/
|
||
Solution 分段由正文自身的 h2/h3 标题承担;不移植词典兜底段落
|
||
(那是 HESU 站点文案,跨站即假数据)。
|
||
2. 侧列数据映射:相册 = make_context 的 gallery(cover + fields.gallery
|
||
去重,与 10b 产品画廊同源);主图 gallery[0],缩略图全量含首图
|
||
(cms 同款,>1 张才渲染缩略图行);无图回退 sf-tp-photo-plain 占位。
|
||
Results 成果卡 = fields.results 开放字段(分号/换行分隔要点;cms 的
|
||
resultsPoints 词典兜底不移植),空则整段隐藏——绝不编造成果;
|
||
✓ 圆徽用 Task 9 check SVG 图标,非文本字符。
|
||
3. ● location 行 = blueprints 为 case 定义的字段 location + industry
|
||
(cms 的 item.text 单行对应物);均空时行隐藏,概览块(眉题 + h2)
|
||
保留 cms 结构位。
|
||
4. Recommended Products = 产品/车型前 4(cms fetchProducts 前 4 同构),
|
||
空则整段隐藏;Other Projects = groups['case'] 排除自身前 4,动作位
|
||
回 /cases/(cms 回 /projects/,平台栏目根)。
|
||
5. 页尾 Cta 横幅由 pro.html 的 .pro-end-cta 承担(10b/10d 同决策);
|
||
cms 的 CreativeWork JSON-LD 不移植(pro.html 的 og:type=article 已覆盖
|
||
分享语义)。 #}
|
||
{% set tp_case_others = groups['case']|rejectattr('route','equalto',page.route)|list %}
|
||
{% set tp_case_products = (groups['product'] + groups['vehicle'])[:4] %}
|
||
{% set tp_results = ((fields.get('results') or '')|string|replace('\r\n','\n')|replace('\r','\n')|replace(';','\n')|trim).split('\n')|map('trim')|reject('equalto','')|list %}
|
||
<section class="sf-tp-page-hero sf-tp-case-hero">
|
||
{% if safe_image(page.cover) %}<img class="sf-tp-page-hero-bg" src="{{ safe_image(page.cover) }}" alt="" width="1600" height="900" fetchpriority="high" decoding="async">{% endif %}
|
||
<div class="sf-tp-page-hero-overlay" aria-hidden="true"></div>
|
||
<div class="wrap">
|
||
<nav class="sf-tp-breadcrumb" aria-label="Breadcrumb" data-reveal>
|
||
<a href="/">Home</a><a href="/cases/">Cases</a><span class="sf-tp-breadcrumb-current">{{ page.title }}</span>
|
||
</nav>
|
||
<div class="sf-tp-page-hero-copy" data-reveal style="--reveal-i: 1">
|
||
<span class="eyebrow">OUR PROJECTS</span>
|
||
<h1>{{ page.title }}</h1>
|
||
{% if page.summary %}<p class="sf-tp-page-hero-lead">{{ page.summary }}</p>{% endif %}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<section class="section sf-tp-case-detail">
|
||
<div class="wrap sf-tp-case-grid">
|
||
<div class="sf-tp-case-copy" data-reveal>
|
||
<div class="sf-tp-case-overview">
|
||
<span class="eyebrow">PROJECT OVERVIEW</span>
|
||
<h2>{{ page.title }}</h2>
|
||
{% if fields.get('location') %}<p class="sf-tp-case-location"><span aria-hidden="true">●</span> {{ fields.get('location') }}</p>{% endif %}
|
||
{% if fields.get('industry') %}<p class="sf-tp-case-location"><span aria-hidden="true">●</span> {{ fields.get('industry') }}</p>{% endif %}
|
||
</div>
|
||
<div class="sf-tp-case-body sf-tp-article-body">{{ body_copy(content, page) }}</div>
|
||
</div>
|
||
<div class="sf-tp-case-side">
|
||
<div class="sf-tp-photo" data-reveal style="--reveal-i: 1">
|
||
{% if gallery %}<img src="{{ gallery[0] }}" alt="{{ page.title }}" width="800" height="600" loading="lazy" decoding="async">
|
||
{% else %}<div class="sf-tp-photo-plain" aria-hidden="true"><span>CASE STUDY</span></div>{% endif %}
|
||
</div>
|
||
{% if gallery|length > 1 %}
|
||
<div class="sf-tp-case-gallery" data-reveal style="--reveal-i: 2" aria-label="Project photo gallery">
|
||
{% for shot in gallery %}<img src="{{ shot }}" alt="{{ page.title }} photo {{ loop.index }}" width="200" height="140" loading="lazy" decoding="async">{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
{% if tp_results %}
|
||
<div class="sf-tp-case-results" data-reveal style="--reveal-i: 2">
|
||
<h3>Results</h3>
|
||
<ul>
|
||
{% for point in tp_results %}<li><span class="sf-tp-case-check" aria-hidden="true">{{ icon('check', size='12') }}</span>{{ point }}</li>{% endfor %}
|
||
</ul>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
{% if tp_case_products %}
|
||
<section class="section section--soft sf-tp-case-products">
|
||
<div class="wrap">
|
||
<div class="sf-tp-section-head" data-reveal>
|
||
<div>
|
||
<span class="eyebrow">APPLICATIONS</span>
|
||
<h2>Recommended products</h2>
|
||
</div>
|
||
</div>
|
||
<div class="sf-tp-card-grid">
|
||
{% for tp_item in tp_case_products %}
|
||
<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 %}
|
||
{% if tp_case_others %}
|
||
<section class="section sf-tp-case-related">
|
||
<div class="wrap">
|
||
<div class="sf-tp-section-head" data-reveal>
|
||
<div>
|
||
<span class="eyebrow">OUR PROJECTS</span>
|
||
<h2>Other projects</h2>
|
||
</div>
|
||
<a class="sf-tp-more" href="/cases/">View all projects {{ icon('arrow-right', cls='sf-tp-icon--sm') }}</a>
|
||
</div>
|
||
<div class="sf-tp-card-grid">
|
||
{% for tp_item in tp_case_others[:4] %}
|
||
<article class="sf-tp-card" data-reveal style="--reveal-i: {{ loop.index0 % 4 }}">
|
||
<div class="sf-tp-card-media">{{ cover(tp_item, 'case') }}</div>
|
||
<div class="sf-tp-card-body">
|
||
<h3>{{ tp_item.title }}</h3>
|
||
<p>{{ (tp_item.summary or 'Project reference 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 %}
|