feat: trust_bar + applications_band blocks; homepage now block-driven
- blueprints: register trust_bar (icon/title/text items) and applications_band (2x2 dark-band tiles) block types; render with escaped items, ordinal icon fallback, 8-item cap; auto-exposed in editor via /api/editor/blocks and grouped under business in the studio palette - blocks.css: platform base layer for both blocks (app/static + trade-pro Layer A kept verbatim in sync) + trade-pro bridge vocabulary (icon-circle, hairline dividers, navy->brand gradient band, glass tiles, 900/620 breakpoints, reduced-motion) without touching canvas hooks - tests: test_trade_pro_home_block_driven covers the full studio-draft PUT -> publish -> build chain; 8 block types on the homepage, template markers absent under layout_mode=replace, clean build report - scripts/setup_home_blocks.py: production write path for site 483295c29ea0 (login -> draft -> publish -> build -> verify); real catalogue counts feed the stats block; run after the Docker image picks up the new block types Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,8 @@ BLOCKS = {
|
||||
'pricing':{'name':'服务方案','icon':'$','fields':['title','items'],'defaults':{'title':'服务方案','items':[{'name':'标准方案','price':'联系我们','features':['专业咨询','售后服务'],'href':'/contact/'}]}},
|
||||
'map':{'name':'位置导航','icon':'⌖','fields':['title','address','url'],'defaults':{'title':'欢迎来访','address':'请填写实际办公地址','url':''}},
|
||||
'form_link':{'name':'询盘表单入口','icon':'✉','fields':['title','description','button','href'],'defaults':{'title':'提交询盘','description':'欢迎联系我们获取报价和资料','button':'联系销售','href':'/contact/'}},
|
||||
'trust_bar':{'name':'信任条','icon':'✓','fields':['title','items'],'defaults':{'title':'','items':[{'icon':'✓','title':'Certified quality','text':'Inspected before every shipment'},{'icon':'◎','title':'Global export','text':'Delivering to worldwide markets'},{'icon':'▤','title':'Full documentation','text':'Specs and export files on hand'},{'icon':'✉','title':'Responsive service','text':'Replies within one business day'}]}},
|
||||
'applications_band':{'name':'应用展示带','icon':'⊞','fields':['title','subtitle','items'],'defaults':{'title':'Built for real-world projects','subtitle':'APPLICATIONS','items':[{'icon':'◉','title':'Water supply','text':'Municipal piping and treatment networks.'},{'icon':'◍','title':'Drainage','text':'Gravity and pressure sewer systems.'},{'icon':'✦','title':'Gas','text':'PE pipelines for fuel gas distribution.'},{'icon':'◆','title':'Mining','text':'Abrasion-resistant slurry and vent lines.'}]}},
|
||||
}
|
||||
# Property contracts shared with the no-code editor. Unknown executable types
|
||||
# are not supported; all values are still verified by validate_blocks.
|
||||
@@ -308,6 +310,17 @@ def render_blocks(blocks, site, page, entries):
|
||||
visible_items=sum(1 for item in items[:12] if isinstance(item,dict))
|
||||
feature_cols=visible_items if 1<=visible_items<=4 else 3
|
||||
parts.append(start+f'<div class="sf-section-head"><h2>{title}</h2></div><div class="sf-features" style="--sf-feature-cols:{feature_cols}">{cards}</div>'+end)
|
||||
elif typ=='trust_bar':
|
||||
# Horizontal trust strip; item icons are free-text glyphs, empty ones
|
||||
# fall back to ordinal markers like the features block.
|
||||
items=block.get('items') if isinstance(block.get('items'),list) else []
|
||||
cells=''.join(f'<div class="sf-trust-item"><span class="sf-trust-icon" aria-hidden="true">{escape(str(item.get("icon") or (i+1)))}</span><strong>{escape(str(item.get("title","")))}</strong><span class="sf-trust-text">{escape(str(item.get("text","")))}</span></div>' for i,item in enumerate(items[:8]) if isinstance(item,dict))
|
||||
head=f'<div class="sf-section-head"><h2>{title}</h2></div>' if title else ''
|
||||
parts.append(start+head+f'<div class="sf-trust-bar">{cells}</div>'+end)
|
||||
elif typ=='applications_band':
|
||||
items=block.get('items') if isinstance(block.get('items'),list) else []
|
||||
tiles=''.join(f'<div class="sf-app-tile"><span class="sf-app-icon" aria-hidden="true">{escape(str(item.get("icon") or (i+1)))}</span><h3>{escape(str(item.get("title","")))}</h3><p>{escape(str(item.get("text","")))}</p></div>' for i,item in enumerate(items[:8]) if isinstance(item,dict))
|
||||
parts.append(start+f'<div class="sf-section-head"><div><span class="sf-kicker">{subtitle}</span><h2>{title}</h2></div></div><div class="sf-applications-band">{tiles}</div>'+end)
|
||||
elif typ=='stats':
|
||||
items=block.get('items') if isinstance(block.get('items'),list) else []
|
||||
cards=''.join(f'<div><strong>{escape(str(item.get("value","")))}</strong><span>{escape(str(item.get("label","")))}</span></div>' for item in items[:12] if isinstance(item,dict))
|
||||
|
||||
Reference in New Issue
Block a user