d60123ba41
- V3.0 总体方案、主题与板块设计、实施计划 - cms 现状差距分析 V2.0(V1.0 已标注作废)与复用评估 V1.0 - 纵向切片设计规格(路线 C:全新架构 + 白名单资产移植) - .gitignore 先行(storage/artifacts/env 永不入库) Co-Authored-By: Claude Code <noreply@anthropic.com>
901 lines
26 KiB
Markdown
901 lines
26 KiB
Markdown
# SiteForge 主题与板块设计方案
|
||
|
||
> 版本:V1.0
|
||
> 日期:2026-10-20
|
||
> 目标:支持每个网站拥有独立设计,同时让内容可以在后台结构化编辑并生成纯静态页面。
|
||
|
||
---
|
||
|
||
## 1. 设计结论
|
||
|
||
主题和板块必须解耦:
|
||
|
||
- **主题(Theme)**负责页面结构、视觉风格、HTML、CSS、动画和响应式实现。
|
||
- **板块(Block)**负责内容字段及其顺序,例如标题、图片、按钮、产品参数、FAQ。
|
||
- **模板(Template)**规定一类页面默认由哪些区域组成,以及允许使用哪些板块。
|
||
- **页面(Page)**保存所选模板和实际板块数据。
|
||
|
||
推荐模式是:
|
||
|
||
```text
|
||
统一板块数据协议 + 每个主题自己的板块渲染器
|
||
```
|
||
|
||
例如后台都保存 `hero` 数据,但汽车站可以显示全屏视频,工业站可以显示左文右图。两者共享内容语义,不共享强制布局。
|
||
|
||
V1 不做任意坐标、任意嵌套的低代码编辑器,只提供:
|
||
|
||
- 选择板块。
|
||
- 表单编辑字段。
|
||
- 调整板块顺序。
|
||
- 启用或隐藏板块。
|
||
- 选择少量经过主题声明的样式变体。
|
||
- PC/移动端静态预览。
|
||
|
||
这样既能快速维护,又不会让自由配置破坏设计、响应式和 SEO。
|
||
|
||
---
|
||
|
||
## 2. 四层结构
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
CONTENT[内容:产品/文章/页面/媒体]
|
||
BLOCK[板块实例:type + data + settings]
|
||
TEMPLATE[页面模板:区域与板块约束]
|
||
THEME[主题:组件、样式、交互]
|
||
HTML[静态 HTML/CSS/JS]
|
||
|
||
CONTENT --> BLOCK
|
||
BLOCK --> TEMPLATE
|
||
TEMPLATE --> THEME
|
||
THEME --> HTML
|
||
```
|
||
|
||
### 2.1 内容层
|
||
|
||
保存可复用的业务数据,例如产品名称、参数、文章正文和媒体资源。内容不应该写死在主题代码里。
|
||
|
||
### 2.2 板块层
|
||
|
||
保存某个页面中的展示内容和顺序。板块数据尽量不包含颜色、像素、CSS 类名等主题实现细节。
|
||
|
||
### 2.3 模板层
|
||
|
||
定义首页、产品详情、新闻详情、普通页面等页面类型及允许的区域。模板可以自动插入由业务数据驱动的系统区域。
|
||
|
||
### 2.4 主题层
|
||
|
||
每个主题提供自己的 Astro 页面、组件、样式和板块渲染器,最终生成静态文件。
|
||
|
||
---
|
||
|
||
## 3. 板块分类
|
||
|
||
### 3.1 全局板块
|
||
|
||
全站复用,单独管理:
|
||
|
||
- Header
|
||
- Footer
|
||
- 顶部通知条
|
||
- 全局询价 CTA
|
||
- 联系方式
|
||
- 社交媒体链接
|
||
- Cookie/隐私提示
|
||
|
||
修改全局板块后,会重建所有引用它的页面;V1 可以直接触发该站全量构建。
|
||
|
||
### 3.2 页面内容板块
|
||
|
||
由管理员在页面中新增、排序和编辑:
|
||
|
||
- Hero
|
||
- Rich Text
|
||
- Image + Text
|
||
- Image Gallery
|
||
- Video
|
||
- Feature List
|
||
- Statistics
|
||
- Logo/Brand List
|
||
- Product Grid
|
||
- Article List
|
||
- Specification Table
|
||
- Download List
|
||
- FAQ
|
||
- Testimonials
|
||
- CTA
|
||
- Contact Information
|
||
- Map Embed
|
||
|
||
### 3.3 模板系统板块
|
||
|
||
由模板根据当前内容自动生成,通常不允许页面编辑者删除:
|
||
|
||
- 面包屑
|
||
- 产品主信息
|
||
- 产品参数
|
||
- 文章标题、日期和正文
|
||
- 分类列表与分页
|
||
- 相关推荐
|
||
- SEO Head
|
||
|
||
管理员可以在允许范围内控制显示、顺序或样式变体,但不直接维护其核心业务数据。
|
||
|
||
### 3.4 站点专属板块
|
||
|
||
某些页面效果只属于一个站点,可以由主题声明自定义板块,例如:
|
||
|
||
```text
|
||
vehicle-360-view
|
||
charging-calculator
|
||
factory-process-timeline
|
||
dealer-map
|
||
```
|
||
|
||
站点专属板块仍需提供数据 schema 和后台表单定义,不能只在代码中写死内容。
|
||
|
||
### 3.5 Legacy HTML 板块
|
||
|
||
用于暂时无法结构化的历史内容:
|
||
|
||
- 只接受清洗后的 HTML。
|
||
- 删除脚本、事件属性和危险 URL。
|
||
- 使用隔离样式容器。
|
||
- 后台醒目标记为“待结构化”。
|
||
- 不能作为新页面的默认编辑方式。
|
||
|
||
---
|
||
|
||
## 4. 板块数据协议
|
||
|
||
### 4.1 板块实例
|
||
|
||
```ts
|
||
export interface BlockInstance<TData = Record<string, unknown>> {
|
||
id: string;
|
||
type: string;
|
||
schemaVersion: number;
|
||
variant?: string;
|
||
data: TData;
|
||
settings?: {
|
||
anchorId?: string;
|
||
themeMode?: 'default' | 'light' | 'dark';
|
||
spacing?: 'none' | 'small' | 'medium' | 'large';
|
||
};
|
||
visibility: {
|
||
enabled: boolean;
|
||
locales?: string[];
|
||
};
|
||
}
|
||
```
|
||
|
||
### 4.2 页面内容示例
|
||
|
||
```json
|
||
{
|
||
"contentId": "page_home",
|
||
"templateKey": "home",
|
||
"blocks": [
|
||
{
|
||
"id": "block_hero_01",
|
||
"type": "hero",
|
||
"schemaVersion": 1,
|
||
"variant": "image-right",
|
||
"data": {
|
||
"eyebrow": "New Energy Vehicles",
|
||
"title": "Build Your Next Journey",
|
||
"description": "A short introduction.",
|
||
"mediaId": "asset_1001",
|
||
"primaryAction": {
|
||
"label": "View Products",
|
||
"href": "/products/"
|
||
}
|
||
},
|
||
"settings": {
|
||
"spacing": "large",
|
||
"themeMode": "dark"
|
||
},
|
||
"visibility": {
|
||
"enabled": true
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 4.3 内容和样式边界
|
||
|
||
建议保存在板块数据中的字段:
|
||
|
||
- 标题、描述和按钮文案。
|
||
- 媒体资源引用。
|
||
- 内部/外部链接。
|
||
- 产品、文章、分类等内容引用。
|
||
- 允许的语义变体,例如 `featured`、`compact`。
|
||
|
||
不建议保存:
|
||
|
||
- 任意 CSS。
|
||
- Tailwind/CSS 类名。
|
||
- 像素坐标。
|
||
- 随意输入的颜色值。
|
||
- 主题组件文件路径。
|
||
- 可执行 JavaScript。
|
||
|
||
颜色、字体和间距默认由主题控制。如果确实需要运营配置,只允许主题 manifest 中声明的有限选项。
|
||
|
||
---
|
||
|
||
## 5. 板块定义与表单生成
|
||
|
||
每个板块类型包含两份声明:
|
||
|
||
1. `schema`:数据校验规则。
|
||
2. `uiSchema`:后台如何展示编辑表单。
|
||
|
||
### 5.1 Hero Schema 示例
|
||
|
||
```json
|
||
{
|
||
"$id": "siteforge://blocks/hero/v1",
|
||
"type": "object",
|
||
"required": ["title"],
|
||
"properties": {
|
||
"eyebrow": { "type": "string", "maxLength": 80 },
|
||
"title": { "type": "string", "minLength": 1, "maxLength": 160 },
|
||
"description": { "type": "string", "maxLength": 500 },
|
||
"mediaId": { "type": "string" },
|
||
"primaryAction": {
|
||
"type": "object",
|
||
"properties": {
|
||
"label": { "type": "string", "maxLength": 40 },
|
||
"href": { "type": "string" }
|
||
},
|
||
"required": ["label", "href"]
|
||
}
|
||
},
|
||
"additionalProperties": false
|
||
}
|
||
```
|
||
|
||
### 5.2 UI Schema 示例
|
||
|
||
```json
|
||
{
|
||
"title": { "widget": "text", "label": "主标题" },
|
||
"description": { "widget": "textarea", "label": "说明" },
|
||
"mediaId": { "widget": "media-picker", "label": "主视觉" },
|
||
"primaryAction.href": { "widget": "link-picker", "label": "按钮链接" }
|
||
}
|
||
```
|
||
|
||
管理后台根据这些声明生成表单,不需要为每个主题重复开发一套内容编辑页面。
|
||
|
||
推荐使用 JSON Schema 作为跨前后端协议,并使用 Ajv 校验。主题内可以生成对应 TypeScript 类型,避免重复维护接口。
|
||
|
||
---
|
||
|
||
## 6. 主题协议
|
||
|
||
### 6.1 主题清单
|
||
|
||
```json
|
||
{
|
||
"id": "automotive-premium",
|
||
"name": "Automotive Premium",
|
||
"version": "1.0.0",
|
||
"engine": "astro",
|
||
"siteConfigSchema": "./schemas/site-config.schema.json",
|
||
"templates": {
|
||
"home": {
|
||
"entry": "src/templates/HomePage.astro",
|
||
"allowedBlocks": ["hero", "feature-list", "product-grid", "cta"],
|
||
"requiredBlocks": ["hero"],
|
||
"maxPerType": { "hero": 1 }
|
||
},
|
||
"product": {
|
||
"entry": "src/templates/ProductDetail.astro",
|
||
"allowedBlocks": ["gallery", "specification-table", "download-list", "faq", "cta"]
|
||
},
|
||
"page": {
|
||
"entry": "src/templates/GenericPage.astro",
|
||
"allowedBlocks": ["hero", "rich-text", "image-text", "gallery", "faq", "cta"]
|
||
}
|
||
},
|
||
"blocks": {
|
||
"hero": {
|
||
"renderer": "src/blocks/HeroBlock.astro",
|
||
"schema": "siteforge://blocks/hero/v1",
|
||
"variants": ["fullscreen", "image-right", "minimal"]
|
||
},
|
||
"product-grid": {
|
||
"renderer": "src/blocks/ProductGridBlock.astro",
|
||
"schema": "siteforge://blocks/product-grid/v1",
|
||
"variants": ["cards", "editorial"]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 6.2 主题目录
|
||
|
||
```text
|
||
themes/automotive-premium/
|
||
theme.json
|
||
schemas/
|
||
site-config.schema.json
|
||
src/
|
||
templates/
|
||
HomePage.astro
|
||
ProductDetail.astro
|
||
ArticleDetail.astro
|
||
GenericPage.astro
|
||
blocks/
|
||
HeroBlock.astro
|
||
RichTextBlock.astro
|
||
ProductGridBlock.astro
|
||
FaqBlock.astro
|
||
components/
|
||
Header.astro
|
||
Footer.astro
|
||
Breadcrumb.astro
|
||
layouts/
|
||
BaseLayout.astro
|
||
styles/
|
||
tokens.css
|
||
global.css
|
||
lib/
|
||
block-renderers.ts
|
||
public/
|
||
```
|
||
|
||
### 6.3 不使用主题继承
|
||
|
||
V1 不建议建立复杂的父主题/子主题覆盖机制。公共能力通过普通包复用:
|
||
|
||
```text
|
||
packages/theme-runtime
|
||
packages/seo-components
|
||
packages/block-contracts
|
||
packages/media-components
|
||
```
|
||
|
||
每个主题仍然明确拥有自己的模板和 CSS。这样升级公共包时更可控,也避免多层覆盖难以追踪。
|
||
|
||
### 6.4 四层组件化复用架构
|
||
|
||
为了提高新主题和新网站的开发速度,组件按复用程度分为四层:
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
C1[第一层:数据契约与工具]
|
||
C2[第二层:无样式基础组件]
|
||
C3[第三层:业务逻辑与 Headless 组件]
|
||
C4[第四层:主题展示组件]
|
||
PAGE[页面模板]
|
||
|
||
C1 --> C2
|
||
C1 --> C3
|
||
C2 --> C4
|
||
C3 --> C4
|
||
C4 --> PAGE
|
||
```
|
||
|
||
#### 第一层:数据契约与工具,完全复用
|
||
|
||
- Block、Page、Media、Menu、SEO 类型。
|
||
- JSON Schema 和数据验证。
|
||
- 内部链接解析。
|
||
- 图片地址、`srcset` 和尺寸计算。
|
||
- URL、Canonical、Hreflang 和结构化数据生成。
|
||
- HTML 净化、日期格式化和多语言工具。
|
||
|
||
这一层不包含任何主题样式。
|
||
|
||
#### 第二层:无样式基础组件,高度复用
|
||
|
||
- Container
|
||
- Grid
|
||
- Stack
|
||
- Media
|
||
- Picture
|
||
- Link
|
||
- Button 基础行为
|
||
- Dialog
|
||
- Accordion
|
||
- Tabs
|
||
- Carousel 控制逻辑
|
||
- Pagination
|
||
|
||
组件提供语义、可访问性和交互状态,不固定颜色、字体和视觉效果。主题通过 CSS、自定义属性和组合方式决定最终外观。
|
||
|
||
#### 第三层:业务逻辑与 Headless 组件,高度复用
|
||
|
||
- 产品列表查询与分页。
|
||
- 文章列表查询。
|
||
- 产品参数分组。
|
||
- 菜单树解析。
|
||
- 面包屑生成。
|
||
- FAQ 展开状态。
|
||
- 表单校验和提交状态。
|
||
- 搜索数据适配。
|
||
- 相关推荐规则。
|
||
|
||
这类组件输出数据或状态,不直接决定最终 DOM 结构。例如 `createProductGridModel()` 负责得到产品卡片数据,汽车主题和工业主题分别决定如何渲染卡片。
|
||
|
||
#### 第四层:主题展示组件,按主题独立
|
||
|
||
- Header、Footer、Mega Menu。
|
||
- Hero 的具体布局。
|
||
- Product Card 的视觉结构。
|
||
- 首页各板块的组合方式。
|
||
- 动画、颜色、字体和响应式细节。
|
||
|
||
这一层允许完全不同,避免为了复用而把所有网站做成同一种风格。
|
||
|
||
### 6.5 公共组件包结构
|
||
|
||
```text
|
||
packages/
|
||
content-contracts/ # Page、Block、Product、Post 等类型
|
||
block-contracts/ # 板块 schema、版本和迁移
|
||
theme-runtime/ # 主题加载、模板解析、渲染上下文
|
||
ui-primitives/ # 无样式基础组件
|
||
headless-components/ # 菜单、分页、Tabs、Accordion 等逻辑
|
||
content-resolvers/ # 产品/文章/分类引用解析
|
||
media-components/ # Picture、Video、资源地址与 srcset
|
||
seo-components/ # Meta、Canonical、Hreflang、JSON-LD
|
||
form-runtime/ # 表单 schema、校验和提交适配
|
||
design-tokens/ # 公共 token 协议,不存具体品牌值
|
||
```
|
||
|
||
主题只引用公共包的正式入口,不能跨主题引用另一个主题内部文件。
|
||
|
||
```text
|
||
允许:theme-a → packages/ui-primitives
|
||
禁止:theme-a → themes/theme-b/src/components/ProductCard.astro
|
||
```
|
||
|
||
如果多个主题确实重复使用一个展示组件,应先将其中的通用行为和结构抽到公共包,再由各主题提供外观;不要直接复制后形成多个无法同步的版本。
|
||
|
||
### 6.6 设计令牌
|
||
|
||
公共组件只使用语义化 token,不使用某个网站的品牌颜色:
|
||
|
||
```css
|
||
:root {
|
||
--sf-color-primary: #1f4b7a;
|
||
--sf-color-accent: #e8792e;
|
||
--sf-color-text: #1b1f24;
|
||
--sf-color-surface: #ffffff;
|
||
--sf-radius-control: 0.5rem;
|
||
--sf-space-section: clamp(4rem, 8vw, 8rem);
|
||
--sf-container-width: 75rem;
|
||
}
|
||
```
|
||
|
||
每个主题覆盖这些 token,也可以增加自己的 token。公共包不得依赖只有某个主题才存在的 token。
|
||
|
||
### 6.7 组合优于大量 Variant
|
||
|
||
公共组件保持小而稳定,通过组合形成页面板块:
|
||
|
||
```astro
|
||
<Section>
|
||
<Container>
|
||
<SplitLayout>
|
||
<HeroContent data={block.data} />
|
||
<ThemeHeroMedia media={media} />
|
||
</SplitLayout>
|
||
</Container>
|
||
</Section>
|
||
```
|
||
|
||
不要给一个公共 Hero 增加几十个布尔属性来适应所有网站,例如 `rounded`、`centered`、`overlay`、`video`、`reverse`、`compact` 的任意组合。稳定的数据协议可以复用,最终板块应由主题组合基础组件实现。
|
||
|
||
### 6.8 组件复用矩阵
|
||
|
||
| 能力 | 公共包复用 | 主题独立 |
|
||
|---|---:|---:|
|
||
| 数据类型与 Schema | 是 | 否 |
|
||
| SEO Head | 是 | 可提供少量站点配置 |
|
||
| Picture/srcset | 是 | 可控制画面比例和样式 |
|
||
| 菜单树和展开逻辑 | 是 | Header 结构和动画独立 |
|
||
| 产品查询与分页 | 是 | 产品卡片独立 |
|
||
| FAQ 数据与交互状态 | 是 | 排版和图标独立 |
|
||
| 表单校验与提交 | 是 | 字段布局和视觉独立 |
|
||
| Hero 内容协议 | 是 | Hero 渲染完全独立 |
|
||
| 页面模板 | 否 | 是 |
|
||
| 品牌色、字体、间距 | 仅协议 | 是 |
|
||
|
||
### 6.9 组件版本与兼容
|
||
|
||
- 公共包遵循语义化版本。
|
||
- 破坏性变更必须提供迁移说明或兼容适配层。
|
||
- 主题 manifest 记录公共运行时的兼容范围。
|
||
- 每个公共组件提供最小、完整、异常三个测试 fixture。
|
||
- 公共组件升级后,自动构建所有示范主题并执行截图回归。
|
||
- 发布 release 锁定公共包和主题版本,回滚时不能自动升级依赖。
|
||
|
||
### 6.10 组件预览与文档
|
||
|
||
建立独立的组件预览页,展示:
|
||
|
||
- 基础组件的状态和可访问性。
|
||
- 每种板块的最小/完整数据。
|
||
- 同一板块在不同主题中的效果。
|
||
- 桌面、平板和手机断点。
|
||
- 空数据、超长标题、图片缺失等异常状态。
|
||
|
||
预览页可以采用 Storybook 或项目内 Astro 组件目录。V1 的重点是可查看和自动截图,不强制依赖某个文档工具。
|
||
|
||
---
|
||
|
||
## 7. 模板约束
|
||
|
||
模板除了渲染页面,还负责约束后台可配置范围。
|
||
|
||
```ts
|
||
export interface TemplateDefinition {
|
||
key: string;
|
||
contentTypes: string[];
|
||
allowedBlocks: string[];
|
||
requiredBlocks?: string[];
|
||
maxPerType?: Record<string, number>;
|
||
lockedRegions?: string[];
|
||
}
|
||
```
|
||
|
||
示例:
|
||
|
||
- 首页必须有一个 Hero,最多一个。
|
||
- 产品详情固定显示产品标题、价格/询价入口和核心参数。
|
||
- FAQ、下载、CTA 可以由管理员添加、删除和排序。
|
||
- SEO Head、Header、Footer 是锁定区域,不出现在自由排序列表里。
|
||
|
||
后台新增板块时,只显示当前主题、当前模板支持的类型和变体。
|
||
|
||
---
|
||
|
||
## 8. 数据库存储
|
||
|
||
推荐将板块实例按修订版本独立存储,而不是直接写入已发布页面。
|
||
|
||
```sql
|
||
CREATE TABLE page_blocks (
|
||
id UUID PRIMARY KEY,
|
||
site_id UUID NOT NULL REFERENCES sites(id),
|
||
revision_id UUID NOT NULL REFERENCES content_revisions(id),
|
||
block_type TEXT NOT NULL,
|
||
schema_version INTEGER NOT NULL,
|
||
variant TEXT,
|
||
position INTEGER NOT NULL,
|
||
data JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||
settings JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||
visibility JSONB NOT NULL DEFAULT '{"enabled":true}'::jsonb,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||
);
|
||
|
||
CREATE INDEX idx_page_blocks_revision_position
|
||
ON page_blocks(revision_id, position);
|
||
```
|
||
|
||
全局板块可以使用:
|
||
|
||
```text
|
||
global_fragments
|
||
global_fragment_revisions
|
||
global_fragment_usages
|
||
```
|
||
|
||
发布时锁定内容修订和全局板块修订,确保历史 release 可以准确回滚。
|
||
|
||
---
|
||
|
||
## 9. 后台编辑体验
|
||
|
||
### 9.1 页面编辑器布局
|
||
|
||
```text
|
||
┌────────────────────────────────────────────────────────────┐
|
||
│ 页面标题 / URL / 模板 / 保存 / 预览 / 发布 │
|
||
├──────────────────────┬─────────────────────────────────────┤
|
||
│ 板块列表 │ 当前板块表单 │
|
||
│ │ │
|
||
│ 1. Hero │ 标题 [________________] │
|
||
│ 2. Product Grid │ 描述 [________________] │
|
||
│ 3. FAQ │ 图片 [选择媒体] │
|
||
│ 4. CTA │ 样式变体 [image-right ▼] │
|
||
│ │ │
|
||
│ [+ 添加板块] │ │
|
||
└──────────────────────┴─────────────────────────────────────┘
|
||
```
|
||
|
||
首版功能:
|
||
|
||
- 点击新增板块。
|
||
- 拖动调整纵向顺序。
|
||
- 复制、隐藏和删除板块。
|
||
- 编辑结构化字段。
|
||
- 从媒体库、产品、文章和内部路由中选择引用。
|
||
- 保存草稿。
|
||
- 创建静态预览。
|
||
- 显示字段和模板校验错误。
|
||
|
||
### 9.2 不在 V1 实现
|
||
|
||
- 画布上任意拖动位置。
|
||
- 任意列宽和像素级布局。
|
||
- 无限层级嵌套板块。
|
||
- 管理员输入 CSS/JavaScript。
|
||
- 在一个主题中自由调用另一个主题的组件。
|
||
|
||
---
|
||
|
||
## 10. Astro 渲染实现
|
||
|
||
### 10.1 渲染器注册
|
||
|
||
```ts
|
||
import HeroBlock from '../blocks/HeroBlock.astro';
|
||
import RichTextBlock from '../blocks/RichTextBlock.astro';
|
||
import ProductGridBlock from '../blocks/ProductGridBlock.astro';
|
||
|
||
export const blockRenderers = {
|
||
hero: HeroBlock,
|
||
'rich-text': RichTextBlock,
|
||
'product-grid': ProductGridBlock,
|
||
} as const;
|
||
```
|
||
|
||
### 10.2 页面循环
|
||
|
||
```astro
|
||
---
|
||
import { blockRenderers } from '../lib/block-renderers';
|
||
|
||
const { page } = Astro.props;
|
||
const blocks = page.blocks.filter((block) => block.visibility.enabled);
|
||
---
|
||
|
||
{blocks.map((block) => {
|
||
const Renderer = blockRenderers[block.type];
|
||
return Renderer
|
||
? <Renderer block={block} page={page} />
|
||
: null;
|
||
})}
|
||
```
|
||
|
||
实际构建前必须已经验证所有板块类型。生产构建发现未知板块时应失败,不能静默生成空白区域;后台预览可以显示“当前主题不支持该板块”的占位提示。
|
||
|
||
### 10.3 数据解析顺序
|
||
|
||
```text
|
||
读取发布快照
|
||
→ 获取页面与内容引用
|
||
→ 获取模板定义
|
||
→ 验证允许的板块和数量
|
||
→ 按 schema 校验每个板块
|
||
→ 解析媒体与内部链接
|
||
→ 选择主题渲染器
|
||
→ 生成 HTML
|
||
→ 执行 SEO/链接/资源验收
|
||
```
|
||
|
||
### 10.4 客户端 JavaScript
|
||
|
||
Astro 默认输出静态 HTML。只有轮播、菜单、筛选等确需交互的组件才使用客户端 hydration,并选择合适的加载策略。普通文本、产品参数和 FAQ 不应无条件加载完整前端框架。
|
||
|
||
---
|
||
|
||
## 11. 主题切换策略
|
||
|
||
### 11.1 可以直接切换的条件
|
||
|
||
- 新主题支持当前页面模板。
|
||
- 新主题支持页面使用的全部板块类型。
|
||
- 每个板块的 schema 版本兼容。
|
||
- 主题所需站点配置完整。
|
||
|
||
### 11.2 切换前检查
|
||
|
||
系统先生成兼容报告:
|
||
|
||
```text
|
||
支持的模板
|
||
缺失的板块渲染器
|
||
不支持的 variant
|
||
缺失的站点配置
|
||
需要迁移的数据版本
|
||
```
|
||
|
||
存在不支持板块时,不允许直接用于生产构建。可以:
|
||
|
||
- 为新主题增加渲染器。
|
||
- 将板块转换为兼容类型。
|
||
- 由管理员明确删除或替换。
|
||
|
||
### 11.3 主题切换不是主要目标
|
||
|
||
平台需要支持主题版本升级和兼容检查,但不必把“任何网站一键换任意主题”作为 V1 目标。不同网站设计差异很大,强行互换会让板块协议变得臃肿。
|
||
|
||
---
|
||
|
||
## 12. 板块版本与数据迁移
|
||
|
||
板块必须携带 `schemaVersion`。
|
||
|
||
```ts
|
||
type BlockMigration = {
|
||
blockType: string;
|
||
fromVersion: number;
|
||
toVersion: number;
|
||
migrate: (oldData: unknown) => unknown;
|
||
};
|
||
```
|
||
|
||
例如 Hero V2 将 `buttonText` 和 `buttonUrl` 合并为 `primaryAction`:
|
||
|
||
```text
|
||
v1 data
|
||
→ 运行纯函数迁移
|
||
→ v2 data
|
||
→ schema 校验
|
||
→ 生成新的内容修订
|
||
```
|
||
|
||
禁止直接批量覆盖已发布修订。迁移失败的板块进入人工处理列表,旧版本数据继续保留。
|
||
|
||
---
|
||
|
||
## 13. API 建议
|
||
|
||
```text
|
||
GET /api/v1/themes
|
||
GET /api/v1/themes/:themeId/manifest
|
||
GET /api/v1/sites/:siteId/theme
|
||
PUT /api/v1/sites/:siteId/theme
|
||
|
||
GET /api/v1/sites/:siteId/pages/:pageId/blocks
|
||
POST /api/v1/sites/:siteId/pages/:pageId/blocks
|
||
PATCH /api/v1/sites/:siteId/pages/:pageId/blocks/:blockId
|
||
DELETE /api/v1/sites/:siteId/pages/:pageId/blocks/:blockId
|
||
POST /api/v1/sites/:siteId/pages/:pageId/blocks/reorder
|
||
POST /api/v1/sites/:siteId/pages/:pageId/validate
|
||
POST /api/v1/sites/:siteId/pages/:pageId/preview
|
||
|
||
GET /api/v1/sites/:siteId/fragments
|
||
POST /api/v1/sites/:siteId/fragments
|
||
PATCH /api/v1/sites/:siteId/fragments/:fragmentId
|
||
```
|
||
|
||
后端负责验证模板、板块类型、schema、variant、媒体和内部链接;不能只在管理后台进行校验。
|
||
|
||
---
|
||
|
||
## 14. 测试策略
|
||
|
||
### 14.1 契约测试
|
||
|
||
- 每个主题 manifest 格式正确。
|
||
- 模板引用的板块都有渲染器。
|
||
- 板块数据能通过对应 schema。
|
||
- 主题声明的 variant 都能渲染。
|
||
- 必需板块和最大数量约束有效。
|
||
|
||
### 14.2 渲染测试
|
||
|
||
- 每个模板至少有一份标准 fixture。
|
||
- 每个板块测试最少字段、完整字段和异常字段。
|
||
- 生成 HTML 不包含预览域名、危险脚本和无效资源。
|
||
- 核心内容在输出 HTML 中真实存在,而非仅客户端加载。
|
||
|
||
### 14.3 视觉测试
|
||
|
||
- 为首页、产品详情、文章详情和普通页面生成桌面/移动截图。
|
||
- 主题版本变化时对比关键页面。
|
||
- 视觉差异由超级管理员确认后才能用于生产发布。
|
||
|
||
### 14.4 性能与可访问性
|
||
|
||
- 图片包含宽高并使用合适尺寸。
|
||
- 交互组件按需加载。
|
||
- 标题层级、链接、按钮和表单具有正确语义。
|
||
- 颜色对比、键盘操作和焦点状态在主题验收中检查。
|
||
|
||
---
|
||
|
||
## 15. V1 实现顺序
|
||
|
||
### Sprint 1:契约和最小渲染
|
||
|
||
- 建立 `block-contracts`。
|
||
- 建立 `ui-primitives`、`theme-runtime` 和 `content-resolvers` 的最小公共包。
|
||
- 定义 Hero、Rich Text、Image Text、Gallery、CTA、FAQ 六个基础板块。
|
||
- 建立主题 manifest 和模板协议。
|
||
- 使用固定 JSON 生成一个静态首页。
|
||
|
||
### Sprint 2:数据库和后台编辑
|
||
|
||
- 建立 `page_blocks` 和内容修订。
|
||
- 根据 schema 生成表单。
|
||
- 支持新增、编辑、删除、排序和隐藏。
|
||
- 支持媒体和内部链接选择。
|
||
|
||
### Sprint 3:主题与预览
|
||
|
||
- 完成第一套主题的首页、普通页面、产品详情和文章详情。
|
||
- 建立预览构建。
|
||
- 增加模板与板块兼容检查。
|
||
|
||
### Sprint 4:第二主题验证
|
||
|
||
- 使用相同基础板块数据开发视觉完全不同的第二主题。
|
||
- 验证哪些字段真正通用,哪些需要站点专属板块。
|
||
- 将两个主题中重复的非视觉逻辑提取到公共组件包。
|
||
- 固化主题 SDK、测试 fixture 和开发文档。
|
||
|
||
### Sprint 5:生产质量
|
||
|
||
- 板块版本迁移。
|
||
- 全局板块。
|
||
- 视觉回归、可访问性和性能检查。
|
||
- 与发布门禁和不可变 release 集成。
|
||
|
||
---
|
||
|
||
## 16. 首版基础板块清单
|
||
|
||
建议首版只实现以下 12 个,真实试点需要后再增加:
|
||
|
||
| 板块 | 核心字段 |
|
||
|---|---|
|
||
| Hero | eyebrow、title、description、media、actions |
|
||
| Rich Text | content |
|
||
| Image Text | title、content、media、mediaPosition、action |
|
||
| Gallery | title、items |
|
||
| Feature List | title、items、icon/media |
|
||
| Statistics | title、items(value/label) |
|
||
| Product Grid | title、productRefs/query、limit、action |
|
||
| Article List | title、category/query、limit、action |
|
||
| Specification Table | title、groups/rows 或 product field binding |
|
||
| Download List | title、assetRefs |
|
||
| FAQ | title、items(question/answer) |
|
||
| CTA | title、description、media、actions |
|
||
|
||
Header、Footer、Breadcrumb、产品主信息和文章主内容作为全局/模板系统组件单独实现,不计入可自由添加板块。
|
||
|
||
---
|
||
|
||
## 17. 开发验收标准
|
||
|
||
主题与板块模块达到以下条件即可视为 V1 完成:
|
||
|
||
1. 超级管理员能为页面选择模板。
|
||
2. 能新增、编辑、排序、隐藏和删除模板允许的板块。
|
||
3. 后台字段由 schema 驱动,并在前后端双重验证。
|
||
4. 相同板块数据能被两套主题渲染成明显不同的设计。
|
||
5. 主题不支持某板块时,生产构建会被阻止并给出明确错误。
|
||
6. 产品详情和文章详情能混合系统板块与可配置板块。
|
||
7. 全局 Header/Footer 修改后能够重新构建站点。
|
||
8. 所有发布版本锁定主题版本和板块修订,可准确回滚。
|
||
9. 页面输出为静态 HTML,核心内容无需 JavaScript 即可读取。
|
||
10. 主题和板块变更进入现有 URL、SEO、内容和视觉发布门禁。
|
||
11. 两套主题复用相同的数据契约、媒体、SEO 和 Headless 逻辑,但最终页面视觉明显不同。
|
||
|
||
---
|
||
|
||
## 18. 最终建议
|
||
|
||
首版主题系统的重点不是“让管理员设计网页”,而是让开发者能够快速制作不同主题,让管理员安全地维护结构化内容。
|
||
|
||
必须守住三个边界:
|
||
|
||
1. 内容数据中不写 CSS 和组件路径。
|
||
2. 主题不能自己决定历史 URL 和 SEO。
|
||
3. 未知或不兼容板块不能在生产构建中静默消失。
|
||
|
||
按“统一协议、独立渲染、有限配置、严格校验”实现,既能保留每个网站的设计自由度,也能让后续内容维护和新站迁移真正复用同一套系统。
|