feat: port cms about layout into trade-pro
Task 10e: rewrite pages/about.html from the 2-row thin variant to the full cms about page design (about.astro, pixel baseline dist/hesupipe/en/about/): page hero with breadcrumb + cover backdrop + in-page anchor button, company intro two-col with derived stats bar, rich body (gated on body), advantages grid, dark mission band with gallery backdrop and glass feature cards, gallery-driven facility card grid (hidden when empty), certifications grid. Data mapping: about page = ordinary page entry; cms dict-only stats and factory copy have no platform fields, so stats reuse the 10a home derivation (products/categories/brands/cases counts, hidden when all empty) and facility cards are driven by page.cover + fields.gallery (make_context gallery, deduped, cover first); markFloatImages is not ported (sanitize strips class attributes, same conclusion as 10d) and the body renders through body_copy + the 10d sf-tp-article-body typography layer via dual classing. CSS lands in pro.css under sf-tp-about-* reusing the 10a/10b/10c components (two-col, stats, feature/card grids, section head, page hero, breadcrumb); responsive collapse comes from the existing 10a/10c media blocks, with about-specific 900/620 rules in a dedicated block (767 intentionally empty, covered by 10c page-hero). Tests: test_trade_pro_about_layout_structure drives a data-rich /about/ (PATCHed bootstrap entry with cover/summary/rich body/gallery + product and post entries) and an empty custom-route about page (meta.page_template), asserting structure, data mapping, and empty-data section hiding. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -401,6 +401,117 @@ def test_trade_pro_article_layout_structure(factory):
|
|||||||
assert not re.search(r'[←→↗↘⌕▾]', ''.join(a.get_text() for a in soup.find_all('a')))
|
assert not re.search(r'[←→↗↘⌕▾]', ''.join(a.get_text() for a in soup.find_all('a')))
|
||||||
|
|
||||||
|
|
||||||
|
def test_trade_pro_about_layout_structure(factory):
|
||||||
|
# Task 10e:about.html 移植 cms 关于页设计(about.astro,像素基准
|
||||||
|
# dist/hesupipe/en/about/)后的结构契约。
|
||||||
|
# 区块序列:PageHero(面包屑 + 封面底图 + 页内锚点按钮)→ 公司介绍两栏
|
||||||
|
# (文案 + 实景图 + 悬浮数据条)→ 富文本正文(仅当 body 存在)→
|
||||||
|
# 优势特性网格 → 深色使命带(实景底图 + 玻璃卡)→ 厂区图集卡(仅当
|
||||||
|
# gallery 存在)→ 资质特性网格。平台事实:about 数据 = 普通页面条目
|
||||||
|
# (title/summary/body/cover + fields.gallery),cms 词典中的 stats/
|
||||||
|
# 厂区文案无对应字段——stats 复用 10a 首页的派生计数(产品/分类/品牌/
|
||||||
|
# 案例),厂区卡片由 page.cover+fields.gallery 驱动,均空则整段隐藏。
|
||||||
|
# 双构建断言:数据齐备的 /about/(bootstrap 必备栏目路由)+ 空数据的
|
||||||
|
# 自定义路由 about 页(meta.page_template 显式指定,不绑定特定路由)。
|
||||||
|
import re
|
||||||
|
sid = site(factory)
|
||||||
|
assert factory.post(f'/api/factory/sites/{sid}/bootstrap').status_code == 200
|
||||||
|
# 数据齐备的 about 条目:PATCH bootstrap 建立的 /about/(保留原路由)
|
||||||
|
entries = factory.get(f'/api/sites/{sid}/entries').json()
|
||||||
|
about_id = next(e['id'] for e in entries if e['route'] == '/about/')
|
||||||
|
cover = 'https://imgs.example/factory-floor.jpg'
|
||||||
|
shot1 = 'https://imgs.example/workshop-line.jpg'
|
||||||
|
shot2 = 'https://imgs.example/finished-goods.jpg'
|
||||||
|
patched = factory.patch(f'/api/sites/{sid}/entries/{about_id}', json={
|
||||||
|
'title': 'About Us', 'summary': 'Manufacturer and export partner since day one.',
|
||||||
|
'body': '<p>Quality control runs from raw material to loaded container.</p><h2>Our standards</h2><p>Every batch is inspected before dispatch.</p>',
|
||||||
|
'cover': cover,
|
||||||
|
'meta': {'fields': {'gallery': f'{shot1}; {shot2}'}}})
|
||||||
|
assert patched.status_code == 200, patched.text
|
||||||
|
# 3 个产品(1 品牌 1 分类)+ 1 篇文章:驱动派生 stats 与 /news/ 链接
|
||||||
|
batch = [{'type': 'product', 'title': f'Valve Series {i}', 'summary': f'Control valve {i}.',
|
||||||
|
'meta': {'fields': {'brand': 'Quanta', 'category': 'Valves'}}} for i in range(3)]
|
||||||
|
batch.append({'type': 'post', 'title': 'Plant Expansion Update', 'summary': 'New extrusion lines.'})
|
||||||
|
import io
|
||||||
|
payload = json.dumps(batch).encode('utf-8')
|
||||||
|
imported = factory.post(f'/api/sites/{sid}/import',
|
||||||
|
files={'file': ('about-batch.json', io.BytesIO(payload), 'application/json')})
|
||||||
|
assert imported.status_code == 200, imported.text[:300]
|
||||||
|
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']
|
||||||
|
|
||||||
|
# ── 数据齐备 /about/:hero + 介绍两栏 + 正文 + 四个设计区块 ──
|
||||||
|
html = factory.get(f'/preview/{sid}/{bid}/about/').text
|
||||||
|
soup = BeautifulSoup(html, 'html.parser')
|
||||||
|
assert len(soup.find_all('h1')) == 1
|
||||||
|
hero = soup.select_one('section.sf-tp-page-hero.sf-tp-about-hero')
|
||||||
|
assert hero is not None
|
||||||
|
bg = hero.select_one('.sf-tp-page-hero-bg')
|
||||||
|
assert bg is not None and 'factory-floor.jpg' in bg.get('src', '')
|
||||||
|
# 面包屑:Home + 当前页标题(非链接)
|
||||||
|
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) == 'About Us'
|
||||||
|
# hero 锚点按钮(cms PageHero button → #main 的页内对应物)
|
||||||
|
assert hero.select_one('.sf-tp-page-hero-copy a.sf-tp-btn[href="#tp-about-intro"]')
|
||||||
|
assert 'Manufacturer and export partner' in hero.select_one('.sf-tp-page-hero-lead').get_text()
|
||||||
|
# 介绍两栏:文案列(摘要 + 有文章时的 /news/ 链接)+ 实景图 + 派生数据条
|
||||||
|
intro = soup.select_one('section.sf-tp-about-intro .sf-tp-two-col')
|
||||||
|
assert intro is not None
|
||||||
|
assert intro.select_one('.sf-tp-copy h2')
|
||||||
|
assert 'Manufacturer and export partner' in intro.select_one('.sf-tp-copy p').get_text()
|
||||||
|
assert intro.select_one('.sf-tp-copy a.sf-tp-btn[href$="/news/"]')
|
||||||
|
photo = intro.select_one('.sf-tp-photo img, .sf-tp-photo .sf-tp-photo-plain')
|
||||||
|
assert photo is not None
|
||||||
|
stats = intro.select('.sf-tp-stats .sf-tp-stat')
|
||||||
|
assert len(stats) >= 2 and 'Products' in intro.select_one('.sf-tp-stats').get_text()
|
||||||
|
# 富文本正文:body 存在才渲染(cms isRichAbout 分支的平台化)
|
||||||
|
body = soup.select_one('.sf-tp-about-body .pro-rich')
|
||||||
|
assert body is not None and 'Quality control' in body.get_text() and 'Our standards' in body.get_text()
|
||||||
|
# 优势 / 使命 / 资质:静态设计内容(cms 词典区块),特性卡带 SVG 图标
|
||||||
|
assert len(soup.select('.sf-tp-about-advantages .sf-tp-feature')) == 4
|
||||||
|
assert all(f.select_one('.icon-circle svg') for f in soup.select('.sf-tp-about-advantages .sf-tp-feature'))
|
||||||
|
mission = soup.select_one('section.section--dark.sf-tp-about-mission')
|
||||||
|
assert mission is not None
|
||||||
|
assert len(mission.select('.sf-tp-feature')) == 4
|
||||||
|
assert mission.select_one('.sf-tp-intro')
|
||||||
|
assert len(soup.select('.sf-tp-about-certs .sf-tp-feature')) == 4
|
||||||
|
# 厂区图集卡:cover + fields.gallery 去重共 3 图
|
||||||
|
facilities = soup.select_one('.sf-tp-about-facilities')
|
||||||
|
assert facilities is not None
|
||||||
|
cards = facilities.select('.sf-tp-about-facility')
|
||||||
|
assert len(cards) == 3
|
||||||
|
srcs = ' '.join(i.get('src', '') for i in facilities.select('img'))
|
||||||
|
assert 'workshop-line.jpg' in srcs and 'finished-goods.jpg' in srcs
|
||||||
|
# 交互箭头一律 SVG(Task 9 契约)
|
||||||
|
assert not re.search(r'[←→↗↘⌕▾]', ''.join(a.get_text() for a in soup.find_all('a')))
|
||||||
|
|
||||||
|
# ── 空数据 about(自定义路由 + meta.page_template 显式指定)──
|
||||||
|
# 无 cover/body/gallery/产品:hero 无底图、正文与图集整段隐藏、stats
|
||||||
|
# 整段隐藏、/news/ 链接不渲染;静态设计区块保留(完整页面而非两栏摘要)。
|
||||||
|
sid2 = site(factory)
|
||||||
|
made = factory.post(f'/api/sites/{sid2}/entries', json={
|
||||||
|
'type': 'page', 'title': 'Our Story', 'route': '/our-story/',
|
||||||
|
'meta': {'page_template': 'about'}})
|
||||||
|
assert made.status_code in (200, 201), made.text
|
||||||
|
assert factory.put(f'/api/factory/sites/{sid2}/theme', json={'theme': 'trade-pro', 'version': '1.0.0'}).status_code == 200
|
||||||
|
bid2 = factory.post(f'/api/sites/{sid2}/builds', json={}).json()['version']
|
||||||
|
bare = factory.get(f'/preview/{sid2}/{bid2}/our-story/')
|
||||||
|
assert bare.status_code == 200, bare.text[:300]
|
||||||
|
bsoup = BeautifulSoup(bare.text, 'html.parser')
|
||||||
|
assert len(bsoup.find_all('h1')) == 1
|
||||||
|
assert bsoup.select_one('section.sf-tp-about-hero') is not None
|
||||||
|
assert bsoup.select_one('.sf-tp-page-hero-bg') is None
|
||||||
|
assert bsoup.select_one('.sf-tp-about-body') is None
|
||||||
|
assert bsoup.select_one('.sf-tp-about-facilities') is None
|
||||||
|
assert bsoup.select_one('.sf-tp-stats') is None
|
||||||
|
assert bsoup.select_one('.sf-tp-about-intro .sf-tp-copy a.sf-tp-btn[href$="/news/"]') is None
|
||||||
|
assert bsoup.select_one('.sf-tp-copy p').get_text(strip=True) # 英文兜底文案
|
||||||
|
assert len(bsoup.select('.sf-tp-about-advantages .sf-tp-feature')) == 4
|
||||||
|
assert bsoup.select_one('section.section--dark.sf-tp-about-mission') is not None
|
||||||
|
assert len(bsoup.select('.sf-tp-about-certs .sf-tp-feature')) == 4
|
||||||
|
|
||||||
|
|
||||||
def test_frontend_entries_include_trade_pro():
|
def test_frontend_entries_include_trade_pro():
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
base = Path(__file__).resolve().parent.parent
|
base = Path(__file__).resolve().parent.parent
|
||||||
|
|||||||
@@ -1,2 +1,175 @@
|
|||||||
<section class="pro-inner-hero pro-info-hero"><div class="pro-wrap"><span class="pro-eyebrow">OUR STORY</span><h1>{{page.title}}</h1><p>{{page.summary}}</p></div></section>
|
{# trade-pro about — cms 关于页设计移植(Task 10e)。
|
||||||
<section class="pro-section pro-info-section"><div class="pro-wrap pro-info-grid"><div><span class="pro-eyebrow">ABOUT / DISCOVER</span><h2>Who we are.</h2><p class="pro-muted">Company profile, capabilities and export experience.</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>
|
区块序列对照 D:/project/cms/runtimes/astro-site-runtime/src/pages/[locale]/about.astro
|
||||||
|
(像素基准 dist/hesupipe/en/about/):
|
||||||
|
PageHero(面包屑 + 封面底图 + 页内锚点按钮)→ 公司介绍两栏(文案 +
|
||||||
|
实景图 + 悬浮数据条)→ 富文本正文(cms 仅富文本渲染,平台 body 存在
|
||||||
|
即渲染)→ 优势特性网格(浅底)→ 使命深色带(实景底图 + 玻璃卡)→
|
||||||
|
厂区图集卡(图集驱动)→ 资质特性网格(浅底)。
|
||||||
|
与 cms 的结构决策:
|
||||||
|
1. about 数据 = 一个普通页面条目(title/summary/body/cover + fields)。
|
||||||
|
cms 词典中的 stats(26+/19+/47/10)与厂区卡文案平台无对应字段:
|
||||||
|
stats 复用 10a 首页关于两栏的派生计数(产品/分类/品牌/案例,全空
|
||||||
|
整段隐藏);厂区卡由 page.cover + fields.gallery(make_context 的
|
||||||
|
gallery 变量,去重含封面)驱动,无图整段隐藏——与首页 about 两栏
|
||||||
|
不同,这是完整页面,静态设计区块(优势/使命/资质)保留 cms 形态。
|
||||||
|
2. cms markFloatImages 构建期给正文图片打浮动 class:平台 sanitize 剥
|
||||||
|
离 class 属性(10d 同结论),不移植;正文统一 body_copy(.pro-rich
|
||||||
|
基线)+ 双挂 sf-tp-article-body 复用 10d 富文本排版层(about.astro
|
||||||
|
的 about-body 与全站 rich-body 同构)。
|
||||||
|
3. hero 的「Our History」按钮 cms 锚到 #main(hero 正下方的内容区),
|
||||||
|
平台模板不在 main 内,锚到介绍两栏的 #tp-about-intro(构建器豁免
|
||||||
|
# 锚点链接);文案列的 history 链接指向 /news/(cms 同款),无文章
|
||||||
|
时隐藏以免空栏目断链。
|
||||||
|
4. cms 使命深色带底图固定 factory.webp:平台用 gallery[0](即封面优先)
|
||||||
|
作实景底图,无图回退 theme.css .section--dark 纯渐变(10a CTA 同策)。
|
||||||
|
5. cms 的 certKicker 等词典文案是设计内容(同首页 trustbar/features
|
||||||
|
处理):移植为英文静态文案,去除 HESU 专属表述(厂名/年份)。 #}
|
||||||
|
<section class="sf-tp-page-hero sf-tp-about-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">ABOUT US</span>
|
||||||
|
<h1>{{ page.title }}</h1>
|
||||||
|
{% if page.summary %}<p class="sf-tp-page-hero-lead">{{ page.summary }}</p>{% endif %}
|
||||||
|
<a class="sf-tp-btn" href="#tp-about-intro">Our history {{ icon('arrow-right') }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="section sf-tp-about-intro" id="tp-about-intro">
|
||||||
|
<div class="wrap sf-tp-two-col">
|
||||||
|
<div class="sf-tp-copy" data-reveal>
|
||||||
|
<span class="eyebrow">COMPANY INTRODUCTION</span>
|
||||||
|
<h2>Who we are</h2>
|
||||||
|
<p>{{ page.summary or site.tagline or 'Company profile, capabilities and export experience.' }}</p>
|
||||||
|
{% if stories %}<a class="sf-tp-btn" href="/news/">Company history {{ icon('arrow-right') }}</a>{% endif %}
|
||||||
|
</div>
|
||||||
|
<div data-reveal style="--reveal-i: 1">
|
||||||
|
<div class="sf-tp-photo">
|
||||||
|
{% if gallery %}<img src="{{ gallery[0] }}" alt="{{ site.name }} facility" width="800" height="600" loading="lazy" decoding="async">
|
||||||
|
{% else %}<div class="sf-tp-photo-plain" aria-hidden="true"><span>EXPORT · B2B</span></div>{% endif %}
|
||||||
|
</div>
|
||||||
|
{% if groups['product'] or groups['vehicle'] or categories or brands or cases %}
|
||||||
|
<div class="sf-tp-stats">
|
||||||
|
{% if groups['product'] or groups['vehicle'] %}<div class="sf-tp-stat"><strong class="mono">{{ groups['product']|length + groups['vehicle']|length }}</strong><span>Products</span></div>{% endif %}
|
||||||
|
{% if categories %}<div class="sf-tp-stat"><strong class="mono">{{ categories|length }}</strong><span>Categories</span></div>{% endif %}
|
||||||
|
{% if brands %}<div class="sf-tp-stat"><strong class="mono">{{ brands|length }}</strong><span>Brands</span></div>{% endif %}
|
||||||
|
{% if cases %}<div class="sf-tp-stat"><strong class="mono">{{ cases|length }}</strong><span>Case studies</span></div>{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% if content %}
|
||||||
|
<section class="section sf-tp-about-body-section">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="sf-tp-section-head" data-reveal>
|
||||||
|
<div>
|
||||||
|
<span class="eyebrow">COMPANY INTRODUCTION</span>
|
||||||
|
<h2>{{ page.title }}</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sf-tp-about-body sf-tp-article-body" data-reveal>{{ body_copy(content, page) }}</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
|
<section class="section section--soft sf-tp-about-advantages">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="sf-tp-section-head" data-reveal>
|
||||||
|
<div>
|
||||||
|
<span class="eyebrow">OUR ADVANTAGES</span>
|
||||||
|
<h2>Why buyers choose us</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sf-tp-feature-grid">
|
||||||
|
{% set tp_advantages = [
|
||||||
|
('check', 'Reliable quality', 'Strict quality control from raw material to loaded container.'),
|
||||||
|
('search', 'Advanced equipment', 'Modern production and testing facilities.'),
|
||||||
|
('phone', 'Professional team', 'Experienced technical and export service teams.'),
|
||||||
|
('location', 'Global service', 'Products delivered to worldwide markets.'),
|
||||||
|
] %}
|
||||||
|
{% for tp_icon, tp_title, tp_desc in tp_advantages %}
|
||||||
|
<div class="sf-tp-feature" data-reveal style="--reveal-i: {{ loop.index0 }}">
|
||||||
|
<div class="icon-circle" aria-hidden="true">{{ icon(tp_icon) }}</div>
|
||||||
|
<h3>{{ tp_title }}</h3>
|
||||||
|
<p>{{ tp_desc }}</p>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="section section--dark sf-tp-about-mission">
|
||||||
|
{% if gallery %}<img class="sf-tp-about-mission-bg" src="{{ gallery[0] }}" alt="" loading="lazy" decoding="async">{% endif %}
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="sf-tp-section-head" data-reveal>
|
||||||
|
<div>
|
||||||
|
<span class="eyebrow">OUR MISSION</span>
|
||||||
|
<h2>Reliable solutions for a better tomorrow</h2>
|
||||||
|
</div>
|
||||||
|
<p class="sf-tp-intro">Our mission is to deliver reliable products and dependable service for global development.</p>
|
||||||
|
</div>
|
||||||
|
<div class="sf-tp-feature-grid">
|
||||||
|
{% set tp_mission = [
|
||||||
|
('quote', 'Our mission', 'Reliable solutions for global development.'),
|
||||||
|
('location', 'Our vision', 'A trusted global partner in our field.'),
|
||||||
|
('check', 'Our values', 'Quality, innovation and sustainability.'),
|
||||||
|
('phone', 'Our promise', 'Long-term value for customers.'),
|
||||||
|
] %}
|
||||||
|
{% for tp_icon, tp_title, tp_desc in tp_mission %}
|
||||||
|
<div class="sf-tp-feature" data-reveal style="--reveal-i: {{ loop.index0 }}">
|
||||||
|
<div class="icon-circle" aria-hidden="true">{{ icon(tp_icon) }}</div>
|
||||||
|
<h3>{{ tp_title }}</h3>
|
||||||
|
<p>{{ tp_desc }}</p>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% if gallery %}
|
||||||
|
<section class="section sf-tp-about-facilities">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="sf-tp-section-head" data-reveal>
|
||||||
|
<div>
|
||||||
|
<span class="eyebrow">FACTORY DISPLAY</span>
|
||||||
|
<h2>Our manufacturing facilities</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sf-tp-card-grid">
|
||||||
|
{% for tp_shot in gallery[:4] %}
|
||||||
|
<figure class="sf-tp-about-facility" data-reveal style="--reveal-i: {{ loop.index0 % 4 }}">
|
||||||
|
<div class="sf-tp-card-media"><img src="{{ tp_shot }}" alt="{{ site.name }} facility {{ loop.index }}" loading="lazy" decoding="async"></div>
|
||||||
|
<figcaption><span class="sf-tp-tag">Facility {{ '%02d'|format(loop.index) }}</span></figcaption>
|
||||||
|
</figure>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
|
<section class="section section--soft sf-tp-about-certs">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="sf-tp-section-head" data-reveal>
|
||||||
|
<div>
|
||||||
|
<span class="eyebrow">CERTIFICATES</span>
|
||||||
|
<h2>International certifications</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sf-tp-feature-grid">
|
||||||
|
{% set tp_certs = [
|
||||||
|
('check', 'ISO 9001', 'Quality management system.'),
|
||||||
|
('location', 'ISO 14001', 'Environmental management system.'),
|
||||||
|
('search', 'ISO 45001', 'Occupational health and safety.'),
|
||||||
|
('download', 'CE', 'European conformity marking.'),
|
||||||
|
] %}
|
||||||
|
{% for tp_icon, tp_title, tp_desc in tp_certs %}
|
||||||
|
<div class="sf-tp-feature" data-reveal style="--reveal-i: {{ loop.index0 }}">
|
||||||
|
<div class="icon-circle" aria-hidden="true">{{ icon(tp_icon) }}</div>
|
||||||
|
<h3>{{ tp_title }}</h3>
|
||||||
|
<p>{{ tp_desc }}</p>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|||||||
@@ -445,3 +445,49 @@
|
|||||||
.sf-theme-trade-pro .sf-tp-article-side-item{grid-template-columns:72px 1fr}
|
.sf-theme-trade-pro .sf-tp-article-side-item{grid-template-columns:72px 1fr}
|
||||||
.sf-theme-trade-pro .sf-tp-article-side-cta{padding:16px}
|
.sf-theme-trade-pro .sf-tp-article-side-cta{padding:16px}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Task 10e:about 关于页(about.astro 移植)──
|
||||||
|
源 src/pages/[locale]/about.astro:PageHero → 公司介绍两栏(实景图 +
|
||||||
|
悬浮数据条)→ 富文本正文 → 优势网格 → 使命深色带(实景底图 + 玻璃
|
||||||
|
特性卡,cms dark-photo/.dark .feature)→ 厂区图集卡(CardGrid cols-4,
|
||||||
|
平台 gallery 驱动)→ 资质网格。共享组件复用 10a/10b/10c 移植成果:
|
||||||
|
sf-tp-two-col/copy/photo/stats(介绍两栏 = 首页关于两栏同构,cms 源码
|
||||||
|
本就同款)、sf-tp-feature-grid/card-grid/section-head(cms 同组件)、
|
||||||
|
sf-tp-page-hero/breadcrumb(10c 内页 hero)、sf-tp-article-body(10d
|
||||||
|
富文本排版层,about-body 与全站 rich-body 同构,markup 双挂类名)。
|
||||||
|
本块只补 about 专属形态:使命带底图叠层与玻璃卡、厂区图集卡。 */
|
||||||
|
|
||||||
|
/* 使命深色带:cms dark-photo = section--dark 渐变 + 实景图(object-position
|
||||||
|
center 55%)。叠层级序(同 page-hero):图(z-1)→ ::before 渐变罩
|
||||||
|
(z-base,复刻 section--dark 渐变盖住图保证文字对比)→ .wrap(z-raised)。
|
||||||
|
无图时罩层叠在原渐变上,视觉与 .section--dark 纯渐变一致。 */
|
||||||
|
.sf-theme-trade-pro .sf-tp-about-mission{position:relative;overflow:hidden}
|
||||||
|
.sf-theme-trade-pro .sf-tp-about-mission-bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center 55%;z-index:calc(var(--sf-tp-z-base) - 1)}
|
||||||
|
.sf-theme-trade-pro .sf-tp-about-mission::before{content:"";position:absolute;inset:0;z-index:var(--sf-tp-z-base);background:linear-gradient(90deg,color-mix(in srgb,var(--sf-tp-color-surface-inverse) 94%,transparent),color-mix(in srgb,var(--sf-tp-color-brand-primary) 60%,transparent))}
|
||||||
|
.sf-theme-trade-pro .sf-tp-about-mission .wrap{position:relative;z-index:var(--sf-tp-z-raised)}
|
||||||
|
/* 玻璃特性卡(cms .dark .feature):半透明白 + 反色字。0,3,0 无条件压过
|
||||||
|
10a 的 .sf-tp-feature 底色(0,2,0),620 横排布局仍由 10a 折叠规则提供 */
|
||||||
|
.sf-theme-trade-pro .sf-tp-about-mission .sf-tp-feature{background:color-mix(in srgb,var(--sf-tp-color-neutral-0) 10%,transparent);border-color:var(--sf-tp-color-border-inverse)}
|
||||||
|
.sf-theme-trade-pro .sf-tp-about-mission .sf-tp-feature h3{color:var(--sf-tp-color-text-inverse)}
|
||||||
|
.sf-theme-trade-pro .sf-tp-about-mission .sf-tp-feature p{color:color-mix(in srgb,var(--sf-tp-color-text-inverse) 82%,transparent)}
|
||||||
|
.sf-theme-trade-pro .sf-tp-about-mission .sf-tp-intro{color:color-mix(in srgb,var(--sf-tp-color-text-inverse) 82%,transparent)}
|
||||||
|
/* 厂区图集卡(cms CardGrid 卡片形态,数据驱动无悬停语义故不复用 .sf-tp-card
|
||||||
|
的链接上抬):媒体复用 sf-tp-card-media(4:3 + 裁剪),编号 tag 补位
|
||||||
|
cms 卡片标题行 */
|
||||||
|
.sf-theme-trade-pro .sf-tp-about-facility{margin:0;background:var(--sf-tp-color-surface-default);border:var(--sf-tp-border-width-hairline) solid var(--sf-tp-color-border-subtle);border-radius:var(--sf-tp-card-border-radius);overflow:hidden;box-shadow:var(--sf-tp-shadow-xs)}
|
||||||
|
.sf-theme-trade-pro .sf-tp-about-facility figcaption{padding:var(--sf-tp-space-3) var(--sf-tp-space-4)}
|
||||||
|
|
||||||
|
/* ── about 响应式(cms 断点 900/620;767 无 about 专属规则——内页 hero
|
||||||
|
的 767 竖版由 10c 既有块覆盖。介绍两栏/数据条/特性网格/图集栅格的折叠
|
||||||
|
全部由 10a 既有块提供(复用同一批类名);10d 的 620 富文本字号经
|
||||||
|
sf-tp-article-body 双挂类名作用于本页正文。折叠规则收在本任务媒体块
|
||||||
|
内的原则只针对本任务新类名(10c 教训:同特异性按源顺序取胜)── */
|
||||||
|
@media (max-width: 900px){
|
||||||
|
.sf-theme-trade-pro .sf-tp-about-mission .sf-tp-intro{max-width:100%}
|
||||||
|
}
|
||||||
|
@media (max-width: 620px){
|
||||||
|
.sf-theme-trade-pro .sf-tp-about-mission h2{font-size:22px}
|
||||||
|
.sf-theme-trade-pro .sf-tp-about-mission .sf-tp-intro{font-size:11px}
|
||||||
|
.sf-theme-trade-pro .sf-tp-about-facility figcaption{padding:9px 10px}
|
||||||
|
.sf-theme-trade-pro .sf-tp-about-facility .sf-tp-tag{font-size:10px}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user