# 前台积木页面（动态路由）

## 概念

- 在 **网页管理** 中创建页面，设置 **前台路径**（`route_path` 或默认 `/{slug}`）、**发布**、**内容模式**。
- **HTML**：`content` 为 HTML 字符串，前台用 `v-html` 渲染（仅信任后台内容）。
- **积木（builder）**：后台使用 **可视化编辑器**（添加模块、**按住左侧手柄拖拽**调整顺序、配置动画）；编辑区右侧为 **实时预览**（与前台共用 `BlockRenderer`）。链接可在弹窗中选择 **站内页面**、**可下载文件** 或 **自定义地址**。亦可展开「高级」直接改 JSON。
- **HTML 模式**：网页管理弹窗内 **左侧源码、右侧 iframe 预览**（沙箱不执行脚本）。
- 存储仍为 JSON，结构如下，前台按模块渲染并支持入场动画。

## 动态路由

- 前台启动时请求 `GET /api/web/routes`，按已发布页面注册 Vue Router。
- `slug` 为 `index` 的页面不参与动态路由（仍由首页 `Home.vue` + 首页数据驱动）。
- 单页数据：`GET /api/web/page?path=/your-path`（`site_id` 可选，默认官网站点）。

## 积木 JSON 结构

```json
{
  "version": 1,
  "blocks": [
    {
      "id": "唯一可选",
      "type": "heading",
      "props": { "text": "标题", "level": 2 },
      "animation": { "enter": "fadeIn", "delay_ms": 0, "duration_ms": 600 }
    }
  ]
}
```

### 模块类型 `type`

| type | props 说明 |
|------|------------|
| `heading` | `text`, `level` (1–6) |
| `text` | `text` 纯文本；或 `html: true` 时用 `html` / `text` 作为 HTML |
| `link_list` | `items: [{ label, url, target? }]` |
| `button` | `text`, `url`, `variant`: `primary` \| `ghost`, `target?` |
| `html` | `html` 原始 HTML 片段 |
| `spacer` | `height` 像素 |
| `divider` | 无 |
| `section` | `padding`, `maxWidth`, `background`；`children` 为子 `blocks` 数组 |

### 动画 `animation.enter`

- `none` | `fadeIn` | `slideUp` | `slideLeft` | `zoomIn`
- `delay_ms`、`duration_ms` 控制延迟与时长（毫秒）

## 扩展新模块

1. 在 `web/src/components/blocks/BlockRenderer.vue` 增加 `type` 分支与样式。
2. 后台仍通过 JSON 配置 `props`，无需改库表结构。

## 首页编辑：下方扩展积木

- 除原有导航、主视觉、特性等表单项外，可增加 **「首页下方扩展区」**：与网页积木相同 JSON，保存后由前台 `Home.vue` 在特性区之后、页脚之前用 **同一套 BlockRenderer** 动态渲染。
- 下载的静态 `index.html` 目前**不包含**该积木区（仅在线 SPA 展示）。

## 首页编辑（导航 / 下载 / 平台链接）

- **管理后台 → 首页编辑与下载**：导航链接、下载按钮链接、各平台链接均可点 **「选择链接」**，与积木编辑器共用 `LinkPickerDialog`。
- **本站页面**：来自当前站点「网页管理」中的页面路径（含首页 `/`）。
- **其他站点首页**：同账号下其他站点；需在 **站点管理** 中填写 **域名**，系统会生成 `https://域名/` 形式的外链。
- **可下载文件**：与积木一致，填入 `/api/web/sites/{siteId}/assets/{id}/download`。
- **试跳**：保存前可在后台用 **「试跳」** 新标签页预览；以 `/` 开头的路径会按当前浏览器域名拼接（与前台实际域名不一致时请以真实站点为准）。
