e017d3a809
- advantages section gains section--soft (white cards were invisible on white) - .pro-art placeholder redesigned: brand gradient + initials + small kind label, ring ornament hidden (was heavy gray block) - plan: new Task 10j (port cms header/footer chrome visuals) per user feedback that overall look should match cms - verified live: mirror build 7f1e8c47f0c4 (374/384 real images pulled, 203 pages, 0 errors) Co-Authored-By: Claude Code <noreply@anthropic.com>
395 lines
23 KiB
Markdown
395 lines
23 KiB
Markdown
# trade-pro 第 5 主题移植实施计划
|
||
|
||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||
|
||
**Goal:** 把 cms Astro 主题移植为 SiteForge V8_10 第 5 套内置主题 trade-pro(英文外贸 B2B),平台适配 + 骨架跑通 + 完整模板 + 验收。
|
||
|
||
**Architecture:** 三步走——先做平台适配(主题发现单一源、宏覆盖机制、主题资产优先、6 处前端入口),再以骨架主题包在真实 hesupipe 站点(`483295c29ea0`,193 条内容)跑通构建,最后按 cms 设计源(`D:\project\cms\runtimes\astro-site-runtime`)逐模板移植视觉。设计依据:[spec R3](../specs/2026-10-22-trade-pro-theme-design.md)。
|
||
|
||
**Tech Stack:** Python 3.12 / FastAPI / Jinja2(FileSystemLoader 多目录)/ pytest / 原生 JS。
|
||
|
||
**环境事实(执行者必读):**
|
||
- 工作目录:`C:\Users\30997\Downloads\SiteForge_Factory_V5_Web_Docker\SiteForge_Enterprise_V8_10`(下称 `$ROOT`,bash 里用正斜杠)
|
||
- Docker 实例已运行:`http://127.0.0.1:8787`(容器 siteforge-v8-10-siteforge-1/-worker-1),admin 密码在 `.env`
|
||
- 测试命令:`python -m pytest -q`(当前 210 项全绿,任何改动后必须保持);JS 检查 `node --check app/static/<file>.js`
|
||
- **宿主机快速循环**(V8_12 起):`PYTHONUTF8=1 .venv/Scripts/python -m pytest -q`,基线 **227 passed + 2 known-fail**(test_p0_backup.py::test_sqlite_backup_and_restore_with_media、test_p0_p4_completion_v89.py::test_restore_replaces_directory_atomically_and_preserves_previous——均为 Windows sqlite 文件锁环境问题,改动前即失败);任何新失败都是回归。权威全量验证用容器:`MSYS_NO_PATHCONV=1 docker run --rm -v "$(pwd -W):/project" -w /project python:3.12-slim sh -c "python -m pip install -q -r requirements-dev.lock && python -m pytest -q"`(容器基线 226 passed + 1 node 环境失败)
|
||
- cms 设计源:`D:/project/cms/runtimes/astro-site-runtime/src`(tokens.css 250 行、base.css 445 行、components/*.astro、pages/[locale]/*.astro);像素基准 `dist/hesupipe/en/`
|
||
- 改动 Python 后需 `docker compose up -d --build` 重建容器再验证 HTTP 层(构建约 1 分钟,有缓存)
|
||
- 契约清单已固化于 spec §2(F1-F13),本计划直接引用
|
||
|
||
---
|
||
|
||
## Phase 1+2:平台适配与骨架跑通(Task 1-8)
|
||
|
||
### Task 1: Git 基线 —— ✅ 已完成(2026-10-22,幂等跳过)
|
||
|
||
已执行并有扩展:仓库初始化、基线提交、远程 `http://219.157.255.213:25311/xinyouhe/cms-SiteForge.git` 配置并推送(main + feat/trade-pro-theme),远程原有 V5 基线历史经 `-s ours` 无损合并,`docs/COMPLETION_PLAN.md` 带入当前树;`.env`/密码确认未入库。随后追加 **V8_12 原地升级**(26 文件覆盖 + bug 修复重放 + compose 项目名钉回 `siteforge-v8-10` 保护数据卷)。执行者无需重跑本任务。
|
||
|
||
### Task 2: 主题发现单一源(F1)
|
||
|
||
**Files:**
|
||
- Modify: `app/core.py:12`
|
||
- Modify: `app/theme_pro.py:16-18`
|
||
- Modify: `app/theme_store.py:14`
|
||
- Test: `tests/test_theme_registry.py`(新建)
|
||
|
||
- [ ] **Step 1: 写失败测试**
|
||
|
||
```python
|
||
"""Theme discovery must be a single source of truth."""
|
||
import json
|
||
from pathlib import Path
|
||
from app import core, theme_pro, theme_store
|
||
|
||
ROOT = Path(core.__file__).resolve().parent.parent
|
||
|
||
def test_discover_returns_builtin_themes_with_manifests():
|
||
ids = core.discover_themes()
|
||
for tid in ('automotive', 'industrial', 'corporate', 'minimal'):
|
||
assert tid in ids
|
||
for tid in ids:
|
||
manifest = json.loads((ROOT / 'themes' / tid / 'manifest.json').read_text(encoding='utf-8'))
|
||
assert manifest.get('id') == tid
|
||
|
||
def test_three_whitelists_agree():
|
||
assert set(core.THEMES) == set(theme_pro.THEMES) == set(theme_store.BUILTIN)
|
||
assert isinstance(core.THEMES, tuple)
|
||
```
|
||
|
||
- [ ] **Step 2:** `python -m pytest tests/test_theme_registry.py -q` → 预期 FAIL(`discover_themes` 不存在)
|
||
- [ ] **Step 3: 实现**——`app/core.py:12` 替换为:
|
||
|
||
```python
|
||
ROOT_PATH = __import__('pathlib').Path(__file__).resolve().parent.parent
|
||
|
||
def discover_themes():
|
||
"""Single source of truth: scan themes/*/manifest.json (id must match folder)."""
|
||
import json
|
||
themes_dir = ROOT_PATH / 'themes'
|
||
found = []
|
||
for folder in sorted(themes_dir.iterdir()):
|
||
if not folder.is_dir() or folder.name.startswith('_'):
|
||
continue
|
||
manifest = folder / 'manifest.json'
|
||
if not manifest.is_file():
|
||
continue
|
||
try:
|
||
if json.loads(manifest.read_text(encoding='utf-8')).get('id') == folder.name:
|
||
found.append(folder.name)
|
||
except (ValueError, UnicodeError):
|
||
continue
|
||
return tuple(found)
|
||
|
||
THEMES = discover_themes()
|
||
```
|
||
|
||
(core.py 顶部已有 `import json`,可复用;实现时去掉重复 import,风格与文件一致。)
|
||
|
||
`app/theme_pro.py:16-18` 改为:
|
||
|
||
```python
|
||
from .core import sanitize, route_path, THEMES
|
||
```
|
||
|
||
(删除原第 18 行的元组定义。)
|
||
|
||
`app/theme_store.py:14` 改为:
|
||
|
||
```python
|
||
from .core import THEMES as BUILTIN
|
||
```
|
||
|
||
- [ ] **Step 4:** `python -m pytest tests/test_theme_registry.py -q` → PASS;`python -m pytest -q` 全绿
|
||
- [ ] **Step 5:** `git add -A && git commit -m "feat: single-source theme discovery replaces three hardcoded whitelists"`
|
||
|
||
### Task 3: theme_pro 每主题环境(宏覆盖机制,F4)
|
||
|
||
**Files:**
|
||
- Modify: `app/theme_pro.py:18-20`(LOADER 旁新增 env_for)
|
||
- Modify: `app/theme_pro.py:190`(render 用 env_for)
|
||
- Test: `tests/test_theme_registry.py`(追加)
|
||
|
||
- [ ] **Step 1: 追加失败测试**
|
||
|
||
```python
|
||
def test_env_for_theme_prefers_theme_dir_then_shared_root():
|
||
from app import theme_pro
|
||
env = theme_pro.env_for('industrial')
|
||
paths = [str(p) for p in env.loader.searchpaths]
|
||
assert paths[0].replace('\\', '/').endswith('themes/industrial')
|
||
assert paths[1].replace('\\', '/').endswith('themes')
|
||
# 共享宏库必须仍可解析(回落路径)
|
||
assert env.get_template('_professional_components.html') is not None
|
||
|
||
def test_env_for_is_cached_per_theme():
|
||
from app import theme_pro
|
||
assert theme_pro.env_for('industrial') is theme_pro.env_for('industrial')
|
||
```
|
||
|
||
- [ ] **Step 2:** 跑测试 → FAIL(env_for 不存在)
|
||
- [ ] **Step 3: 实现**——`app/theme_pro.py` 在 LOADER 定义后追加:
|
||
|
||
```python
|
||
_ENVS = {}
|
||
|
||
def env_for(theme):
|
||
"""Per-theme Jinja env: theme dir first, shared themes/ root as fallback.
|
||
|
||
Lets a theme override shared macros (_professional_components.html) while
|
||
inheriting everything else. Cached: one env per theme across 5000-page builds.
|
||
"""
|
||
env = _ENVS.get(theme)
|
||
if env is None:
|
||
theme_dir = ROOT / 'themes' / theme
|
||
search = [str(theme_dir), str(ROOT / 'themes')] if theme_dir.is_dir() else [str(ROOT / 'themes')]
|
||
env = Environment(loader=FileSystemLoader(search), autoescape=select_autoescape(['html', 'xml']))
|
||
_ENVS[theme] = env
|
||
return env
|
||
```
|
||
|
||
`render()` 末行(:190)改为:
|
||
|
||
```python
|
||
return env_for(theme).get_template('pro.html').render(**ctx)
|
||
```
|
||
|
||
- [ ] **Step 4:** `python -m pytest -q` 全绿(现有 test_theme_pro_v3.py 即回归)
|
||
- [ ] **Step 5:** commit `feat: per-theme Jinja env enables theme-level macro override`
|
||
|
||
### Task 4: builder 主题资产优先(F5)
|
||
|
||
**Files:**
|
||
- Modify: `app/builder.py:191`
|
||
- Test: `tests/test_theme_registry.py`(追加)
|
||
|
||
- [ ] **Step 1: 追加失败测试**
|
||
|
||
```python
|
||
def test_theme_blocks_css_overrides_platform(tmp_path):
|
||
from app import builder
|
||
from pathlib import Path
|
||
dist = tmp_path / 'assets'; dist.mkdir()
|
||
theme_dir = tmp_path / 'theme'; theme_dir.mkdir()
|
||
(theme_dir / 'blocks.css').write_text('/* theme version */', encoding='utf-8')
|
||
builder.copy_blocks_css(theme_dir, dist)
|
||
assert (dist / 'blocks.css').read_text(encoding='utf-8') == '/* theme version */'
|
||
# 无主题版时回落平台版
|
||
dist2 = tmp_path / 'assets2'; dist2.mkdir()
|
||
builder.copy_blocks_css(tmp_path / 'nonexistent', dist2)
|
||
assert (dist2 / 'blocks.css').is_file()
|
||
```
|
||
|
||
- [ ] **Step 2:** 跑 → FAIL
|
||
- [ ] **Step 3: 实现**——`app/builder.py:191` 的 `shutil.copy2(ROOT/'app'/'static'/'blocks.css',dist/'assets'/'blocks.css')` 替换为调用新函数,并在文件合适位置(output_path 附近)定义:
|
||
|
||
```python
|
||
def copy_blocks_css(theme_dir, dist_assets):
|
||
"""Theme-provided blocks.css wins; platform default is the fallback."""
|
||
src = theme_dir / 'blocks.css' if (theme_dir / 'blocks.css').is_file() else ROOT / 'app' / 'static' / 'blocks.css'
|
||
shutil.copy2(src, dist_assets / 'blocks.css')
|
||
```
|
||
|
||
- [ ] **Step 4:** `python -m pytest -q` 全绿;commit `feat: theme-level blocks.css takes priority over platform asset`
|
||
|
||
### Task 5: trade-pro 骨架主题包(Phase 2 核心)
|
||
|
||
**Files:**
|
||
- Create: `themes/trade-pro/manifest.json`、`site.html`、`pro.html`、`_professional_components.html`、`theme.css`、`blocks.css`、`pro.css`、`page-presets.json`、`pages/`(14 个 html)、`visual/.gitkeep`、`versions/.gitkeep`
|
||
- Test: `tests/test_theme_registry.py`(追加)
|
||
|
||
- [ ] **Step 1: 追加失败测试**
|
||
|
||
```python
|
||
def test_trade_pro_registered_and_compatible():
|
||
assert 'trade-pro' in core.THEMES
|
||
result = theme_store.compatibility('trade-pro')
|
||
assert result['valid'], result['errors']
|
||
assert not result['errors']
|
||
|
||
def test_trade_pro_builds_real_site(tmp_path):
|
||
# 复用 tests/test_theme_pro_v3.py 中"建站→切主题→构建→断言预览页"的既有写法
|
||
# (夹具工厂在 tests/test_factory_v2.py:15-30),theme 用 'trade-pro'。
|
||
# 产物读取走夹具的 HTTP 形式(与 v3 一致):
|
||
html = factory.get(f'/preview/{sid}/{bid}/').text
|
||
assert 'lang="en"' in html
|
||
assert '>首页<' not in html
|
||
```
|
||
|
||
(执行时先读 test_theme_pro_v3.py 的相邻测试,逐行复制其夹具调用方式,仅替换 theme 参数与断言;禁止保留 `...` 空壳。)
|
||
|
||
- [ ] **Step 2:** 跑 → FAIL
|
||
- [ ] **Step 3: 创建文件**
|
||
|
||
`manifest.json`:
|
||
|
||
```json
|
||
{
|
||
"id": "trade-pro",
|
||
"name": "Trade Pro — International B2B",
|
||
"description": "English-first international B2B theme ported from the hesupipe Astro design: SVG icon set, responsive trade catalogue, product specs and inquiry CTA.",
|
||
"version": "1.0.0",
|
||
"style": "international-b2b",
|
||
"entry": "pro.html",
|
||
"styles": ["theme.css", "blocks.css"],
|
||
"page_templates": ["home","about","contact","listing","category","product","article","brand","case","service","faq","standard","notfound"],
|
||
"blocks": ["hero","richtext","features","stats","collection","image_text","gallery","faq","cta","attributes","spacer","layout","carousel","video","tabs","pricing","map","form_link"],
|
||
"responsive": true,
|
||
"auto_binding": true,
|
||
"professional_layout": true,
|
||
"commercial_ui": true,
|
||
"accent": "#0b3d91"
|
||
}
|
||
```
|
||
|
||
`site.html`:以 `themes/industrial/site.html` 为底(它是 4.4KB 的完整独立模板,自带 header/footer 结构,**不引用共享宏库**),改 `lang="zh-CN"` → `lang="en"`。注意:pro 主题的正式渲染走 pro.html,site.html 主要服务非 pro 路径/兜底。
|
||
|
||
**已知新行为(评审发现,记录不阻断)**:Task 2 后 trade-pro 进入 BUILTIN,上传主题 ZIP 可声明 `layout_family:'trade-pro'`(theme_store.install 会拷贝 trade-pro 的 site/pro.html),而 ZIP 包渲染走共享宏回落——与依赖自有宏库的 trade-pro 存在潜在错配。本轮接受(上传包本就以声明式为主),后续如出问题在 install() 校验 layout_family 白名单。
|
||
|
||
`_professional_components.html`:先做**透传骨架**(从共享库复制 card/cover 等宏,Task 10 起逐个换成 cms 设计 + SVG 图标)。
|
||
|
||
`pro.html`:以 industrial 的 pro.html 为底最小化:`lang="en"`;页眉/页脚消费 site_design chrome 但按 spec 4.2.5 值比对输出英文兜底(中文默认值视为未配置);正文区渲染 page content 与 custom_blocks;按钮类名一律 `sf-tp-*`。
|
||
|
||
`theme.css`:先放 tokens 骨架(Task 9 全量移植):
|
||
|
||
```css
|
||
:root{--sf-tp-primary:#0b3d91;--sf-tp-accent:#f5a623;--sf-tp-bg:#ffffff;--sf-tp-text:#1a2332;--sf-tp-muted:#5a6a7e;--sf-tp-radius:8px;--sf-tp-font:'Inter',system-ui,-apple-system,sans-serif}
|
||
```
|
||
|
||
`blocks.css`:空文件 + 注释 `/* trade-pro component styles: filled in Phase 3 */`。
|
||
`pro.css`:空文件(兼容性硬性要求,spec F8)。
|
||
`page-presets.json`:**复制 industrial 的同名文件原样**(Phase 4 精修;13 种 key 齐全即可)。
|
||
`pages/`:`home.html` 写最小可用骨架(hero + showcase 列表 + cta 引用共享宏);其余 13 个(about/contact/listing/product/brand/article/case/service/faq/category/search/standard/notfound)先各写薄变体(复用 home 的结构、引用同样宏),文件必须存在(spec F7 硬性)。
|
||
|
||
- [ ] **Step 4:** `python -m pytest -q` 全绿(含新测试)
|
||
- [ ] **Step 5:** `docker compose up -d --build` 后:`curl -s http://127.0.0.1:8787/api/health` 版本不变、`curl -s "http://127.0.0.1:8787/api/factory/themes/trade-pro/compatibility"`(带登录 cookie)返回 valid=true
|
||
- [ ] **Step 6:** commit `feat: trade-pro skeleton theme package (Phase 2)`
|
||
|
||
### Task 6: theme_color 映射 + 6 处前端入口(F3/F11)
|
||
|
||
**Files:**
|
||
- Modify: `app/theme_pro.py`(theme_color 字典,make_context 内,约 :171)
|
||
- Modify: `app/templates/admin.html:13`(siteTheme select)
|
||
- Modify: `app/templates/workspace.html:41`(建站模态框 theme select)
|
||
- Modify: `app/templates/theme-gallery.html:4`(theme select)
|
||
- Modify: `app/static/workspace.js:5`(themeName)
|
||
- Modify: `app/static/admin.js:5`(cfg)
|
||
- Modify: `app/static/factory-v2.js:30`(schemes)
|
||
- Test: `tests/test_theme_registry.py`(追加)
|
||
|
||
- [ ] **Step 1: 追加失败测试**
|
||
|
||
```python
|
||
def test_frontend_entries_include_trade_pro():
|
||
import pathlib
|
||
base = pathlib.Path(__file__).resolve().parent.parent
|
||
for rel, needle in [
|
||
('app/templates/admin.html', 'value="trade-pro"'),
|
||
('app/templates/workspace.html', 'value="trade-pro"'),
|
||
('app/templates/theme-gallery.html', 'value="trade-pro"'),
|
||
('app/static/workspace.js', "'trade-pro'"),
|
||
('app/static/admin.js', "'trade-pro'"),
|
||
('app/static/factory-v2.js', "'trade-pro'"),
|
||
]:
|
||
assert needle in (base / rel).read_text(encoding='utf-8'), rel
|
||
|
||
def test_theme_color_map_covers_trade_pro():
|
||
# 真实断言:trade-pro 构建出的首页含 meta theme-color #0b3d91
|
||
# (复用 Task 5 的构建夹具与 /preview/ HTTP 读取写法)
|
||
html = factory.get(f'/preview/{sid}/{bid}/').text
|
||
assert '<meta name="theme-color" content="#0b3d91"' in html
|
||
```
|
||
|
||
- [ ] **Step 2:** 跑 → FAIL
|
||
- [ ] **Step 3: 实现**——逐处追加(保持原文风格):
|
||
- theme_color 字典追加 `'trade-pro':'#0b3d91'`
|
||
- 三个 HTML select 各追加:`<option value="trade-pro">Trade Pro · 外贸 B2B(英文)</option>`(theme-gallery 用 `Trade Pro · International B2B`)
|
||
- `workspace.js` themeName 追加 `'trade-pro':'Trade Pro'`
|
||
- `admin.js` cfg 追加 `trade-pro:{label:'Trade Pro',desc:'英文外贸 · B2B 目录与询盘',prev:'corp',title:'Go global.'}`
|
||
- `factory-v2.js` schemes 追加 `'trade-pro':['#0b3d91','#e8f1fb','GO GLOBAL']`
|
||
- [ ] **Step 4:** `python -m pytest -q` 全绿 + `for f in app/static/*.js; do node --check "$f"; done` 通过
|
||
- [ ] **Step 5:** commit `feat: register trade-pro in theme color map and all six frontend entry points`
|
||
|
||
### Task 7: E2E 验收——真实站点切主题构建(Phase 2 门槛)
|
||
|
||
**Files:** 无代码(验证 + 记录)
|
||
|
||
- [ ] **Step 1:** 登录拿 cookie/CSRF(参照已验证流程:POST /api/auth/login → GET /api/auth/me)
|
||
- [ ] **Step 2:** `PUT /api/factory/sites/483295c29ea0/theme` body `{"theme":"trade-pro"}`(确认返回成功)
|
||
- [ ] **Step 3:** `POST /api/sites/483295c29ea0/builds`(Idempotency-Key + CSRF)→ 轮询 job 至 done
|
||
- [ ] **Step 4:** 断言:pages ≥203、errors 空;预览首页 HTTP 200、含 `lang="en"`、不含"首页/联系我们"(平台中文默认,spec F9 验收);`/products/`、`/product/alloy-resin-system-window/` 200
|
||
- [ ] **Step 5:** 断言 6 入口:新工作台/`/classic` 建站表单、theme-gallery、factory-studio 主题卡里均出现 trade-pro(浏览器人工点验,截图存 `docs/superpowers/` 或记录于验收文件)
|
||
- [ ] **Step 6:** 把结果写入 `docs/superpowers/specs/2026-10-22-trade-pro-theme-design.md` 附录"Phase 2 验收记录";commit
|
||
|
||
---
|
||
|
||
## Phase 3:完整视觉移植(Task 8-12)
|
||
|
||
> 以下任务的代码无法在计划里全文预写(从 cms 源逐段移植),但每步给出**精确源文件、提取规则与验收断言**。共用纪律:每完成一个 Task 跑全量 pytest + commit。
|
||
|
||
### Task 8: 设计令牌与基础样式全量移植
|
||
|
||
- 源:`D:/project/cms/runtimes/astro-site-runtime/src/styles/tokens.css`(250 行)+ `base.css`(445 行)
|
||
- [ ] 变量重命名加 `--sf-tp-` 前缀(spec 4.2.6);按钮/交互元素全部 `sf-tp-` 类名(避开平台 `.btn/.primary/.request/.button/.nav-btn`)
|
||
- [ ] 验收断言:`themes/trade-pro/theme.css` 覆盖 tokens 全集(颜色/字体/圆角/间距);构建产物 `assets/theme.css` 含 `--sf-tp-` 变量;平台 custom.css 的 `.btn{...}` 与主题按钮类无交集(grep 断言)
|
||
- [ ] pytest(现有快照不受影响)+ commit
|
||
|
||
### Task 9: 图标宏库(新增设计——不是 cms 移植)
|
||
|
||
> 评审修正(2026-10-22):cms 源码与 dist 实测 **0 个 SVG**,其图标实为文本箭头 `→`(index 15 处、product 17 处)。用户需求"图标需要优化、现在很多都是箭头"意味着图标是**超出 cms 源的新增设计**,不与 dist 对照。
|
||
|
||
- [ ] `themes/trade-pro/_professional_components.html` 定义 `icon(name)` 宏:自设计内联 SVG 集合,线条风格与 `--sf-tp-*` 令牌一致,≥8 个:arrow-right / check / phone / mail / location / download / search / quote
|
||
- [ ] 尺寸/描边随 CSS 变量(`currentColor`、`1.5px` stroke),跟随深浅文字色
|
||
- [ ] 验收:home/product 渲染中**交互元素的图标均为 SVG**,不再以裸 `→` 作为唯一图标(对照对象是本主题自身,非 dist);`aria-hidden="true"` 保证可访问性
|
||
- [ ] commit
|
||
|
||
### Task 10: 九种独立布局逐个移植(每布局一个 commit)
|
||
|
||
顺序与源对照(cms `src/pages/[locale]/` ↔ dist `en/` ↔ trade-pro `pages/`):
|
||
|
||
1. `home`:index.astro ↔ dist 首页(Hero/FeatureGrid/CardGrid/CTA 结构)
|
||
2. `product`:products/[slug].astro ↔ dist/products/alloy-resin-system-window/(参数表 specs、多图 gallery、询盘 CTA)
|
||
3. `listing`:news/[...page].astro + products 列表 ↔ 按 kind 分支(Phase 0 已确认模板可拿到 page/kind 上下文;若不可分支由 category 承担产品列表,spec §7 兜底)
|
||
4. `category`:product-category ↔ 分类页
|
||
5. `article`:news/[slug] ↔ 文章详情
|
||
6. `about` / 7. `contact`(LeadForm 样式 + form_endpoint)/ 8. `case`(projects)/ 9. `standard`(applications、support)
|
||
|
||
每个布局的统一验收:构建对应真实页面 200;关键结构选择器与 dist 对照(header/hero/卡片/参数表/页脚);英文文案断言(无中文默认);手机宽度可读(人工抽查一次,全量视觉验收在 Task 12)。
|
||
|
||
### Task 11: 五个变体布局 + search
|
||
|
||
- brand/service/faq/notfound/search 以 standard/listing 变体补齐为"结构合理"版本(后续迭代精修,spec 4.3)
|
||
|
||
### Task 12: 预设、编辑器与全量验收(Phase 4)
|
||
|
||
- [ ] `page-presets.json` 13 种按 cms 首页/产品页实际区块结构重写(编辑器初始布局与真实设计一致)
|
||
- [ ] 编辑器验证:hero/cta/features/collection/attributes 在 trade-pro 下可编辑、预览、保存草稿、发布(spec §6)。**已知观察(Task 4 质量审查)**:main.py 编辑器侧读平台版 blocks.css,而构建产物可能用主题版——编辑器预览与最终站点可能样式不一致;本项验收时确认影响,必要时编辑器预览也走主题版
|
||
- [ ] 全站设计草稿验证:页眉/页脚/导航配置生效(显式值渲染、中文默认被值比对替换为英文兜底)
|
||
- [ ] 全量回归:`python -m pytest -q`(≥210+新增全绿)、`node --check` 全部 JS
|
||
- [ ] 实战验收:站点 483295c29ea0 最终构建 ≥203 页零错误;视觉对照两层标准(spec §6)逐页记录
|
||
- [ ] 更新 spec 附录验收记录;commit
|
||
|
||
## 与 spec 的已记录偏差(评审裁定可接受)
|
||
|
||
1. **发现函数放 core 而非 spec 4.2.1 写的 theme_store**:等价实现。core 已被全部引用方(builder/main/theme_pro)导入,放 core 消除反向耦合;theme_store.BUILTIN 经 import 别名保持一致。
|
||
2. **builder.ENV 保持单目录**(spec 4.2.4 提到两处构造点):已验证 site.html 是不引用共享宏的独立模板,且带主题前缀的路径加载在单目录下安全;仅 theme_pro.LOADER 改为 per-theme env_for。
|
||
3. **spec §6 "渲染快照"以 E2E 页面断言实现**(构建后逐页 200 + 结构选择器 + 文案断言),不另建 Jinja 快照测试文件。
|
||
4. **/api/themes(main.py:267)经 core.THEMES 动态化自动生效**,不设专项测试(catalog 由既有测试覆盖)。
|
||
5. **前端入口 option 文案**:三个 HTML 下拉用"Trade Pro · 外贸 B2B(英文)"中文风格与各文件现状一致(theme-gallery 用英文)。
|
||
6. **平台数据层中文残留(Task 5 质量审查发现)**:bootstrap 生成的 8 个标准页(/about/ /contact/ /products/ /news/ 等)的 `<title>`、`<h1>`、面包屑来自平台造的中文标题(关于我们/联系我们/产品中心…),主题层无法翻译用户数据。Phase 3 视觉验收时接受(真实站点内容为英文导入数据时自然英文),若 bootstrap 英文化成为需求属平台任务另行立项。
|
||
|
||
## 用户反馈修复队列(2026-10-23 首页预览反馈,10b 收口后立即执行)
|
||
|
||
0. **[新增 10j] cms 页眉/页脚移植**(用户反馈"整体不是 cms 效果"):在平台 site_design 生成的 DOM 结构上,用 .sf-theme-trade-pro 作用域 CSS 完整还原 cms Header/Footer 视觉(Header.astro/Footer.astro 源码样式:白底细分割线、导航字距/悬停态、蓝色 GET A QUOTE 按钮、cms 页脚深色带与栏目布局)。保持平台 chrome 数据流(导航/公告/CTA 配置仍生效)。排在 10c-10i 之后、Task 11 之前。
|
||
|
||
1. `.sf-tp-feature` 补 cms FeatureGrid.astro 的卡片皮肤:白底(--sf-tp-color-surface-default)+ hairline 边框(--sf-tp-color-border-subtle)+ 圆角 + 居中 + padding,900px 两列、620px 横排(源码 scoped CSS 全量对照)
|
||
2. `.sf-tp-card` 对照 cms dist 的 .card scoped CSS 补边框卡片皮肤(含 hover 抬升已有则保留)
|
||
3. `.sf-theme-trade-pro` 作用域内重设计 `.pro-art` 无图占位:brand 渐变底(surface-inverse → brand-primary 混合)+ 白色首字母居中 + 小写 kind 标签,替代共享宏灰块("10 PRODUCT"噪字)
|
||
4. Hero 双按钮改横排(flex gap,620px 换行)——核对 pro.css hero 区现状
|
||
5. 验收方式:浏览器截图前后对照(Playwright),产品卡/特性卡/占位块三处逐一确认
|
||
|
||
## 风险与回退
|
||
|
||
- 每个 Task 独立 commit,出问题 `git revert` 单点回退
|
||
- 平台适配层(Task 2-4)有 4 主题回归保护:任何现有测试变红立即停止
|
||
- trade-pro 目录独立,删目录 + revert 白名单改动即完全移除
|