feat: port cms standard layout into trade-pro
Task 10h (last regular layout). Design sources: cms applications.astro + support.astro (generic content pages). Common form = PageHero (eyebrow/h1/ lead + in-page anchor button + cover bg) then content area. - The structured sections in both sources (application card grid, FeatureGrid bands, anchor nav strip, doc cards, FAQ) are HESU dictionary content, not layout - not ported (fake data across sites). The platform standard page content is the rich-text body: section structure is carried by the body's own h2/img/tables (10g decision), typeset via the 10d sf-tp-article-body layer double-attached with sf-tp-standard-body. - Empty body hides the body section and the hero anchor button (cms isRichAbout ternary); empty cover/summary hide bg/lead independently. - Breadcrumb Home > title per platform page-level convention (10e/10f); support's trailing Cta stays covered by pro.html pro-end-cta. - Zero new CSS rules: applications.astro itself ships no scoped styles and the template is pure composition (10c hero / 10b breadcrumb / 10a section-head / theme.css baselines); decision documented in pro.css. - Test test_trade_pro_standard_layout_structure: build-flow page entry asserts hero/body containers; bootstrap /services/ page (meta. page_template='standard') verifies the template is not route-bound (explicit meta beats route-prefix inference) and the empty-data hiding. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -722,6 +722,82 @@ def test_trade_pro_case_layout_structure(factory):
|
||||
assert bsoup.select_one('.sf-tp-case-location') is None # 字段空则行隐藏
|
||||
|
||||
|
||||
def test_trade_pro_standard_layout_structure(factory):
|
||||
# Task 10h:standard.html 移植 cms 通用内容页设计(applications.astro +
|
||||
# support.astro,像素基准 dist/hesupipe/en/{applications,support}/)后的
|
||||
# 结构契约。standard 是所有非专属 kind 的兜底模板,两页共性形态:
|
||||
# PageHero(眉题/h1/导语/页内锚点按钮 + 封面底图)→ 内容区。
|
||||
# 与 cms 的结构决策:两页内容区的应用卡网格 / FeatureGrid / 锚点导航 /
|
||||
# 文档卡 / FAQ 全是 HESU 词典内容而非布局——跨站即假数据,不移植;
|
||||
# 平台 standard 页内容 = 富文本 body,小节结构由正文自身 h2 承担
|
||||
# (10g 案例详情同决策),排版层双挂 sf-tp-article-body(10d)。
|
||||
# body 为空 → 正文区块与 hero 锚点按钮整段隐藏(cms isRichAbout 三元
|
||||
# 分支同款)。meta.page_template='standard' 优先于路由前缀推断
|
||||
# (infer_kind 显式分支先于 /services/ 前缀)——模板不绑路由:
|
||||
# bootstrap 的 /services/ 页(meta standard)渲染本模板而非 service 模板。
|
||||
import re
|
||||
sid = site(factory)
|
||||
assert factory.post(f'/api/factory/sites/{sid}/bootstrap').status_code == 200
|
||||
made = factory.post(f'/api/sites/{sid}/entries', json={
|
||||
'type': 'page', 'title': 'Applications', 'route': '/applications/',
|
||||
'cover': 'https://imgs.example/water-treatment.jpg',
|
||||
'summary': 'Piping systems for infrastructure, energy, industry and agriculture.',
|
||||
'body': '<p>Our systems serve municipal, agricultural and industrial projects worldwide.</p><h2>Municipal engineering</h2><p>Reliable piping for urban utility networks.</p>'})
|
||||
assert made.status_code in (200, 201), made.text
|
||||
assert factory.put(f'/api/factory/sites/{sid}/theme', json={'theme': 'trade-pro', 'version': '1.0.0'}).status_code == 200
|
||||
bid = factory.post(f'/api/sites/{sid}/builds', json={}).json()['version']
|
||||
# ── 主流程:普通页面条目(无 meta.page_template,infer_kind 兜底回落
|
||||
# standard)→ hero + 正文区块 ──
|
||||
r = factory.get(f'/preview/{sid}/{bid}/applications/')
|
||||
assert r.status_code == 200, r.text[:400]
|
||||
html = r.text
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
# hero:面包屑(Home › 标题)+ 封面底图 + 眉题 + 唯一 h1 + 导语
|
||||
hero = soup.select_one('section.sf-tp-page-hero.sf-tp-standard-hero')
|
||||
assert hero is not None
|
||||
bg = hero.select_one('.sf-tp-page-hero-bg')
|
||||
assert bg is not None and 'water-treatment.jpg' in bg.get('src', '')
|
||||
assert hero.select_one('.sf-tp-breadcrumb a')
|
||||
current = hero.select_one('.sf-tp-breadcrumb-current')
|
||||
assert current is not None and current.get_text(strip=True) == 'Applications'
|
||||
assert hero.select_one('.sf-tp-page-hero-copy[data-reveal] h1')
|
||||
assert 'Piping systems for infrastructure' in hero.select_one('.sf-tp-page-hero-lead').get_text()
|
||||
assert len(soup.find_all('h1')) == 1
|
||||
# cms PageHero 页内锚点按钮(applications/support 均有):锚到正文区块,
|
||||
# 箭头为 SVG(Task 9 契约)
|
||||
btn = hero.select_one('.sf-tp-page-hero-copy a.sf-tp-btn[href="#tp-standard-body"]')
|
||||
assert btn is not None and btn.select_one('svg') is not None
|
||||
# 正文区块:SectionHead(眉题 + 页题 h2——cms about-body 的 SectionHead
|
||||
# title={heroTitle} 同构)+ 富文本正文(10d 排版层双挂),正文 h2 承担小节
|
||||
body_section = soup.select_one('section.sf-tp-standard-body-section#tp-standard-body')
|
||||
assert body_section is not None
|
||||
head = body_section.select_one('.sf-tp-section-head')
|
||||
assert head is not None and head.select_one('.eyebrow') and head.find('h2')
|
||||
body = body_section.select_one('.sf-tp-standard-body.sf-tp-article-body .pro-rich')
|
||||
assert body is not None and 'Municipal engineering' in body.get_text()
|
||||
# 旧骨架(Phase 2 pro-info 双栏)无残留
|
||||
assert 'pro-info-hero' not in html and 'pro-info-section' not in html
|
||||
# 交互箭头一律 SVG(Task 9 契约)
|
||||
assert not re.search(r'[←→↗↘⌕▾]', ''.join(a.get_text() for a in soup.find_all('a')))
|
||||
# 本构建链接全通(面包屑与锚点按钮不产生断链)
|
||||
report = factory.get(f'/api/sites/{sid}/builds/{bid}/report').json()
|
||||
assert not report['errors'] and not report['broken_links'] and not report['missing_local_images']
|
||||
|
||||
# ── 空数据 + 模板不绑路由:bootstrap 的 /services/ 页 meta.page_template
|
||||
# ='standard'(显式 meta 优先于路由前缀推断),无 body → 正文区块与
|
||||
# 锚点按钮整段隐藏;渲染 standard 模板而非 service 模板 ──
|
||||
services = factory.get(f'/preview/{sid}/{bid}/services/')
|
||||
assert services.status_code == 200, services.text[:300]
|
||||
ssoup = BeautifulSoup(services.text, 'html.parser')
|
||||
assert len(ssoup.find_all('h1')) == 1
|
||||
assert ssoup.select_one('section.sf-tp-page-hero.sf-tp-standard-hero') is not None
|
||||
assert 'pro-info-hero' not in services.text # service 模板骨架未渲染
|
||||
assert ssoup.select_one('.sf-tp-standard-body-section') is None # 无 body 整段隐藏
|
||||
assert '#tp-standard-body' not in services.text # 锚点按钮随正文隐藏
|
||||
assert ssoup.select_one('.sf-tp-page-hero-bg') is None # 无封面 → 无底图
|
||||
assert ssoup.select_one('.sf-tp-page-hero-lead') is not None # bootstrap 摘要在导语位
|
||||
|
||||
|
||||
def test_frontend_entries_include_trade_pro():
|
||||
from pathlib import Path
|
||||
base = Path(__file__).resolve().parent.parent
|
||||
|
||||
@@ -1,2 +1,58 @@
|
||||
<section class="pro-inner-hero pro-info-hero"><div class="pro-wrap"><span class="pro-eyebrow">COMPANY / EXPLORE</span><h1>{{page.title}}</h1><p>{{page.summary}}</p></div></section>
|
||||
<section class="pro-section pro-info-section"><div class="pro-wrap pro-info-grid"><div><span class="pro-eyebrow">OVERVIEW</span><h2>{{page.title}}</h2><p class="pro-muted">Applications, support material and company information.</p></div><div class="pro-info-copy">{{body_copy(content,page)}}{% if page.cover %}<img class="pro-info-photo" src="{{safe_image(page.cover)}}" alt="{{page.title}}">{% endif %}</div></div></section>
|
||||
{# trade-pro standard — cms 通用内容页设计移植(Task 10h,最后一个常规布局)。
|
||||
standard 是所有非专属 kind 页面的兜底模板(应用/支持/隐私政策/自定义页等),
|
||||
设计源 = cms 的两个通用内容页 D:/project/cms/runtimes/astro-site-runtime/
|
||||
src/pages/[locale]/applications.astro 与 support.astro(像素基准
|
||||
dist/hesupipe/en/applications/ 与 /en/support/)。
|
||||
两页共性模板形态(standard 的设计本质):
|
||||
PageHero(眉题 + h1 + 导语 + 页内锚点按钮 + 封面底图)→ 内容区。
|
||||
与 cms 的结构决策:
|
||||
1. 内容区的「布局 vs 内容」判定:applications 的应用卡网格(cols-3)、
|
||||
FeatureGrid 优势带、projects 卡带,与 support 的锚点导航条 / 文档卡
|
||||
(含 PDF 体积等假数据位)/ FAQ 折叠,全是 HESU 词典驱动的站点内容
|
||||
而非布局——跨站即假数据,不移植。平台 standard 页内容 = 单一富文本
|
||||
body:小节结构由正文自身的 h2/h3/图片/表格承担(10g 案例详情同
|
||||
决策),排版层双挂 sf-tp-article-body(10d;cms about-body 与全站
|
||||
rich-body 同构)。正文区块头沿用 cms about-body 的 SectionHead
|
||||
title={heroTitle} 形态(眉题 + 页题 h2 复述,10e 同款)。
|
||||
2. body 为空 → 正文区块整段隐藏(cms isRichAbout ? ... : null 同款),
|
||||
hero 锚点按钮随之隐藏(无正文即无锚点目标,不留死锚点);封面/摘要
|
||||
为空则底图/导语各自隐藏,页面退化为纯 hero 最小形态。
|
||||
3. 两页 PageHero 均无面包屑(crumbs 仅 cms 详情页经 slot 传入),但平台
|
||||
页面级模板约定补面包屑 Home › 标题(10e/10f 同款平台化决策;cms
|
||||
landing 页的锚点导航条不移植——sanitize 剥离正文 id,无锚点目标)。
|
||||
4. hero 按钮是 cms PageHero 的固有形态(applications「Explore Products
|
||||
→ #main」/ support「Contact Technical Support → #technical」),
|
||||
平台锚到正文区块 #tp-standard-body(构建器豁免 # 锚点链接),
|
||||
文案取通用词(页面专属词典文案不移植)。
|
||||
5. support 页尾的 Cta 横幅由 pro.html 的 .pro-end-cta 全局承担
|
||||
(10b/10d/10g 同决策)。applications.astro 本身零 scoped CSS——
|
||||
全部由共享组件构成,本模板同构复用(10c hero / 10b 面包屑 / 10a
|
||||
section-head / 10d 排版层),无新增样式规则。 #}
|
||||
<section class="sf-tp-page-hero sf-tp-standard-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><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">OVERVIEW</span>
|
||||
<h1>{{ page.title }}</h1>
|
||||
{% if page.summary %}<p class="sf-tp-page-hero-lead">{{ page.summary }}</p>{% endif %}
|
||||
{% if content %}<a class="sf-tp-btn" href="#tp-standard-body">Explore this page {{ icon('arrow-right') }}</a>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% if content %}
|
||||
<section class="section sf-tp-standard-body-section" id="tp-standard-body">
|
||||
<div class="wrap">
|
||||
<div class="sf-tp-section-head" data-reveal>
|
||||
<div>
|
||||
<span class="eyebrow">OVERVIEW</span>
|
||||
<h2>{{ page.title }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sf-tp-standard-body sf-tp-article-body" data-reveal style="--reveal-i: 1">{{ body_copy(content, page) }}</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
@@ -573,3 +573,19 @@
|
||||
.sf-theme-trade-pro .sf-tp-case-results{padding:16px}
|
||||
.sf-theme-trade-pro .sf-tp-case-results li{font-size:var(--sf-tp-font-size-xs)}
|
||||
}
|
||||
|
||||
/* ── Task 10h:standard 标准内容页(applications.astro + support.astro 移植)──
|
||||
源 src/pages/[locale]/applications.astro 与 support.astro(通用内容页,
|
||||
像素基准 dist/hesupipe/en/{applications,support}/):两页共性形态 =
|
||||
PageHero(眉题/h1/导语/页内锚点按钮 + 封面底图)→ 内容区。
|
||||
本任务零新增样式规则——设计源同构事实:applications.astro 自身零 scoped
|
||||
CSS(100% 由 PageHero/SectionHead/CardGrid/FeatureGrid 共享组件构成),
|
||||
support.astro 的 scoped CSS(锚点导航/doc 卡/FAQ 双栏)全绑 HESU 词典
|
||||
数据,不移植(跨站即假数据)。本模板同样纯组合复用:
|
||||
sf-tp-page-hero/bg/overlay/copy/lead(10c 内页 hero,无封面时浅品牌
|
||||
渐变兜底)、sf-tp-breadcrumb(10b)、eyebrow/sf-tp-btn(theme.css
|
||||
基线)、sf-tp-section-head(10a)、sf-tp-article-body 排版层双挂
|
||||
(10d,sf-tp-standard-body 为结构与测试钩子类)。sf-tp-standard-hero/
|
||||
-body-section 仅作模板定位钩子,无视觉规则;响应式(767/620 hero 竖版
|
||||
与正文紧凑字号)全部由 10c/10d 既有媒体块经共用类名覆盖——无自有媒体块
|
||||
即无级联顺序风险(10c 教训:同特异性按源顺序取胜)。 #}
|
||||
|
||||
Reference in New Issue
Block a user