# 可视化主题设计

<cite>
**本文引用的文件**
- [design.html](file://public/themes/admin_default/admin/theme/design.html)
- [scripts.html](file://public/themes/admin_default/admin/theme/scripts.html)
- [file_widget_setting.html](file://public/themes/admin_default/admin/theme/file_widget_setting.html)
- [widget_setting.html](file://public/themes/admin_default/admin/theme/widget_setting.html)
- [ThemeController.php](file://vendor/thinkcmf/cmf-api/src/admin/controller/ThemeController.php)
- [manifest.json（文章控件）](file://public/themes/default/public/widgets/article/manifest.json)
- [manifest.json（特性控件）](file://public/themes/default/public/widgets/features/manifest.json)
- [manifest.json（默认主题）](file://public/themes/default/manifest.json)
- [draggable.js](file://public/static/js/draggable.js)
</cite>

## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考量](#性能考量)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)

## 简介
本文件面向 ThinkCMF 的“可视化主题设计”能力，聚焦“自由控件（Widget）系统”的设计理念与实现原理，系统性阐述以下主题：
- 拖拽布局编辑器的工作机制：控件的拖放、排序与位置调整
- 实时预览功能：前端预览与后端渲染的同步机制
- 控件配置面板：属性设置、样式调整与数据绑定
- 主题设计器的用户界面与操作流程
- 可视化编辑的数据持久化机制
- 自定义控件开发指南与最佳实践
- 响应式设计与移动端适配

## 项目结构
可视化主题设计由“后台设计器界面 + 前端模拟器 + 控件清单 + 后端 API + 主题文件持久化”构成。核心文件分布如下：
- 后台设计器页面与脚本：design.html、scripts.html
- 控件配置页：file_widget_setting.html、widget_setting.html
- 控件清单与元信息：各控件目录下的 manifest.json
- 后端接口：ThemeController.php（主题与文件、控件设置相关）
- 前端交互与拖拽：draggable.js、jQuery UI Sortable（在 scripts.html 中使用）

```mermaid
graph TB
subgraph "后台设计器"
D["design.html<br/>设计器容器与按钮"]
S["scripts.html<br/>拖拽/排序/颜色选择等逻辑"]
FWS["file_widget_setting.html<br/>文件级控件区块管理"]
WS["widget_setting.html<br/>控件属性配置表单"]
end
subgraph "主题与控件"
TM["ThemeController.php<br/>文件/控件设置API"]
W1["manifest.json(文章控件)"]
W2["manifest.json(特性控件)"]
T["manifest.json(默认主题)"]
end
subgraph "前端模拟器"
IF["iframe 模拟器<br/>__ROOT__/design.html?_design_theme=..."]
JS["draggable.js<br/>拖拽基础库"]
end
D --> IF
D --> S
S --> TM
FWS --> TM
WS --> TM
TM --> W1
TM --> W2
TM --> T
S --> JS
```

**图表来源**
- [design.html:147-173](file://public/themes/admin_default/admin/theme/design.html#L147-L173)
- [scripts.html:22-107](file://public/themes/admin_default/admin/theme/scripts.html#L22-L107)
- [file_widget_setting.html:84-106](file://public/themes/admin_default/admin/theme/file_widget_setting.html#L84-L106)
- [widget_setting.html:30-35](file://public/themes/admin_default/admin/theme/widget_setting.html#L30-L35)
- [ThemeController.php:404-455](file://vendor/thinkcmf/cmf-api/src/admin/controller/ThemeController.php#L404-L455)
- [manifest.json（文章控件）:1-16](file://public/themes/default/public/widgets/article/manifest.json#L1-L16)
- [manifest.json（特性控件）:1-83](file://public/themes/default/public/widgets/features/manifest.json#L1-L83)
- [manifest.json（默认主题）:1-11](file://public/themes/default/manifest.json#L1-L11)
- [draggable.js:1-152](file://public/static/js/draggable.js#L1-L152)

**章节来源**
- [design.html:1-262](file://public/themes/admin_default/admin/theme/design.html#L1-L262)
- [scripts.html:22-107](file://public/themes/admin_default/admin/theme/scripts.html#L22-L107)
- [file_widget_setting.html:1-106](file://public/themes/admin_default/admin/theme/file_widget_setting.html#L1-L106)
- [widget_setting.html:1-35](file://public/themes/admin_default/admin/theme/widget_setting.html#L1-L35)
- [ThemeController.php:404-455](file://vendor/thinkcmf/cmf-api/src/admin/controller/ThemeController.php#L404-L455)
- [manifest.json（文章控件）:1-16](file://public/themes/default/public/widgets/article/manifest.json#L1-L16)
- [manifest.json（特性控件）:1-83](file://public/themes/default/public/widgets/features/manifest.json#L1-L83)
- [manifest.json（默认主题）:1-11](file://public/themes/default/manifest.json#L1-L11)
- [draggable.js:1-152](file://public/static/js/draggable.js#L1-L152)

## 核心组件
- 设计器容器与预览
  - 设计器页面包含顶部工具按钮（刷新、打开设置面板、设备切换）与右侧设置面板，左侧为 iframe 预览器。
  - 预览器通过 URL 参数携带当前主题标识，进入“设计模式”。
- 控件区块与控件
  - 主题文件支持多区块（widgets_blocks），每个区块可添加多个控件实例。
  - 控件由 manifest.json 描述其元信息、变量与默认样式。
- 控件配置面板
  - 支持控件标题、变量（文本、富文本、数组等）与 CSS 外观（边距等）的即时配置。
- 后端 API
  - 提供文件设置读取/保存、控件设置读取/保存、模板文件列表等接口。
- 拖拽与排序
  - 使用 jQuery UI Sortable 对控件进行跨区块拖拽与同区块排序；提交后触发预览刷新。

**章节来源**
- [design.html:147-173](file://public/themes/admin_default/admin/theme/design.html#L147-L173)
- [scripts.html:22-107](file://public/themes/admin_default/admin/theme/scripts.html#L22-L107)
- [file_widget_setting.html:84-106](file://public/themes/admin_default/admin/theme/file_widget_setting.html#L84-L106)
- [widget_setting.html:30-35](file://public/themes/admin_default/admin/theme/widget_setting.html#L30-L35)
- [ThemeController.php:404-455](file://vendor/thinkcmf/cmf-api/src/admin/controller/ThemeController.php#L404-L455)
- [manifest.json（特性控件）:12-82](file://public/themes/default/public/widgets/features/manifest.json#L12-L82)

## 架构总览
可视化主题设计采用“前后端分离 + iframe 预览”的架构：
- 前端：设计器页面负责控件拖拽、排序、配置面板展示与保存触发。
- 后端：ThemeController 提供文件与控件设置的读取与保存接口，持久化到主题文件记录。
- 预览：iframe 加载前台页面，携带设计态参数，后端根据参数返回设计态视图；保存后刷新预览以同步最新数据。

```mermaid
sequenceDiagram
participant U as "设计师"
participant D as "设计器页面(design.html)"
participant S as "脚本(scripts.html)"
participant API as "ThemeController.php"
participant IF as "预览iframe"
U->>D : 打开设计器
D->>IF : 加载带主题参数的页面
U->>S : 拖拽/排序控件
S->>API : 提交控件排序/配置
API-->>S : 返回成功
S->>D : 触发预览刷新
D->>IF : 刷新预览
U->>D : 点击保存
D->>S : 触发设置面板确认
S->>API : 保存文件/控件设置
API-->>S : 成功
S->>D : 关闭设置面板
```

**图表来源**
- [design.html:174-262](file://public/themes/admin_default/admin/theme/design.html#L174-L262)
- [scripts.html:22-107](file://public/themes/admin_default/admin/theme/scripts.html#L22-L107)
- [ThemeController.php:482-604](file://vendor/thinkcmf/cmf-api/src/admin/controller/ThemeController.php#L482-L604)

**章节来源**
- [design.html:137-262](file://public/themes/admin_default/admin/theme/design.html#L137-L262)
- [scripts.html:22-107](file://public/themes/admin_default/admin/theme/scripts.html#L22-L107)
- [ThemeController.php:482-604](file://vendor/thinkcmf/cmf-api/src/admin/controller/ThemeController.php#L482-L604)

## 详细组件分析

### 组件A：设计器容器与预览同步
- 功能要点
  - 顶部按钮：刷新当前页面、打开设置面板、切换设备（PC/PAD/MOBILE）。
  - 设置面板：右侧固定面板，包含 iframe 内的配置表单，底部保存按钮。
  - 预览器：左侧 iframe，加载前台页面并注入设计态参数。
  - 心跳刷新：定时向后端发送请求保持会话活跃。
- 数据流
  - 用户点击保存 → 设置面板确认 → 调用子 iframe 的 confirm → 后端保存 → 刷新预览。

```mermaid
flowchart TD
Start(["进入设计器"]) --> OpenPanel["打开设置面板"]
OpenPanel --> Edit["编辑控件/区块"]
Edit --> SaveClick{"点击保存？"}
SaveClick --> |是| Confirm["调用设置面板confirm()"]
Confirm --> Post["提交设置到后端"]
Post --> Refresh["刷新预览"]
SaveClick --> |否| Continue["继续编辑"]
Continue --> Edit
Refresh --> End(["完成"])
```

**图表来源**
- [design.html:147-262](file://public/themes/admin_default/admin/theme/design.html#L147-L262)
- [scripts.html:174-262](file://public/themes/admin_default/admin/theme/scripts.html#L174-L262)

**章节来源**
- [design.html:1-262](file://public/themes/admin_default/admin/theme/design.html#L1-L262)
- [scripts.html:174-262](file://public/themes/admin_default/admin/theme/scripts.html#L174-L262)

### 组件B：控件区块与拖拽排序
- 功能要点
  - 每个文件可包含多个区块（widgets_blocks），每个区块可添加控件。
  - 使用 jQuery UI Sortable 实现跨区块拖拽与同区块排序。
  - 排序完成后，按文件与区块聚合控件顺序，提交到后端接口。
- 交互流程
  - 拖拽结束（stop）→ 收集每个区块内的控件顺序 → 发送排序请求 → 刷新预览。

```mermaid
sequenceDiagram
participant U as "设计师"
participant F as "文件控件设置页"
participant S as "scripts.html"
participant API as "ThemeController.php"
U->>F : 在区块间拖拽控件
F->>S : 触发sortable stop事件
S->>S : 聚合文件ID/区块名/控件ID序列
S->>API : POST 排序数据
API-->>S : 返回成功
S->>S : 刷新父级预览器
```

**图表来源**
- [scripts.html:22-71](file://public/themes/admin_default/admin/theme/scripts.html#L22-L71)
- [ThemeController.php:57-65](file://vendor/thinkcmf/cmf-api/src/admin/controller/ThemeController.php#L57-L65)

**章节来源**
- [scripts.html:22-71](file://public/themes/admin_default/admin/theme/scripts.html#L22-L71)
- [ThemeController.php:57-65](file://vendor/thinkcmf/cmf-api/src/admin/controller/ThemeController.php#L57-L65)

### 组件C：控件配置面板（属性/样式/数据）
- 功能要点
  - 控件属性：标题、变量（文本、富文本、数组等）、CSS 外观（边距等）。
  - 表单渲染：基于控件 manifest.json 的 vars/css 定义生成。
  - 验证与保存：提交前按规则校验，保存后清缓存并刷新预览。
- 数据绑定
  - 控件值从文件记录读取，回填到表单；提交时合并到 more.widgets 下对应控件项。

```mermaid
flowchart TD
Load["读取控件manifest与已有值"] --> Render["渲染配置表单"]
Render --> Change["用户修改属性/CSS"]
Change --> Validate{"校验通过？"}
Validate --> |否| ShowErr["提示错误"]
ShowErr --> Render
Validate --> |是| Submit["提交到后端"]
Submit --> Persist["持久化到文件more.widgets"]
Persist --> Clear["清理缓存"]
Clear --> Reload["刷新预览"]
```

**图表来源**
- [widget_setting.html:30-35](file://public/themes/admin_default/admin/theme/widget_setting.html#L30-L35)
- [ThemeController.php:682-754](file://vendor/thinkcmf/cmf-api/src/admin/controller/ThemeController.php#L682-L754)
- [ThemeController.php:782-806](file://vendor/thinkcmf/cmf-api/src/admin/controller/ThemeController.php#L782-L806)

**章节来源**
- [widget_setting.html:1-35](file://public/themes/admin_default/admin/theme/widget_setting.html#L1-L35)
- [ThemeController.php:682-754](file://vendor/thinkcmf/cmf-api/src/admin/controller/ThemeController.php#L682-L754)
- [ThemeController.php:782-806](file://vendor/thinkcmf/cmf-api/src/admin/controller/ThemeController.php#L782-L806)

### 组件D：控件清单与元信息
- 功能要点
  - 每个控件在主题 public/widgets/<name>/ 下提供 manifest.json，描述控件元信息、默认变量与默认样式。
  - 特性控件示例展示了复杂变量结构（数组嵌套）与字段规则。
- 作用
  - 后端读取 manifest.json 作为控件配置表单的“蓝图”，并与文件记录中的实际值合并显示与保存。

```mermaid
erDiagram
THEME_FILE {
int id PK
string theme
string file
json more
}
WIDGET_MANIFEST {
string name PK
string title
json vars
json css
}
THEME_FILE ||--o{ WIDGET_MANIFEST : "引用"
```

**图表来源**
- [manifest.json（文章控件）:1-16](file://public/themes/default/public/widgets/article/manifest.json#L1-L16)
- [manifest.json（特性控件）:1-83](file://public/themes/default/public/widgets/features/manifest.json#L1-L83)

**章节来源**
- [manifest.json（文章控件）:1-16](file://public/themes/default/public/widgets/article/manifest.json#L1-L16)
- [manifest.json（特性控件）:1-83](file://public/themes/default/public/widgets/features/manifest.json#L1-L83)

### 组件E：拖拽基础库与交互
- 功能要点
  - draggable.js 提供基础拖拽能力（鼠标按下/移动/释放、边界限制、选择清除）。
  - 在设计器中，控件拖拽主要依赖 jQuery UI Sortable（scripts.html），但 draggable.js 可用于其他场景的拖拽组件。
- 适用场景
  - 当需要更底层的拖拽控制或自定义拖拽行为时，可结合 draggable.js 使用。

**章节来源**
- [draggable.js:1-152](file://public/static/js/draggable.js#L1-L152)
- [scripts.html:22-107](file://public/themes/admin_default/admin/theme/scripts.html#L22-L107)

## 依赖关系分析
- 前端依赖
  - 设计器页面依赖 jQuery UI Sortable 进行控件排序；依赖 ColorPicker 进行颜色选择；依赖 iframe 与父窗口通信。
- 后端依赖
  - ThemeController 依赖主题文件模型（ThemeFileModel）与国际化模型（ThemeFileI18nModel）持久化控件与文件设置。
- 控件依赖
  - 控件清单（manifest.json）决定配置表单结构与默认值；后端据此渲染与校验。

```mermaid
graph LR
S["scripts.html"] --> API["ThemeController.php"]
D["design.html"] --> S
API --> M["ThemeFileModel"]
API --> MI["ThemeFileI18nModel"]
API --> WM["控件manifest.json"]
```

**图表来源**
- [scripts.html:22-107](file://public/themes/admin_default/admin/theme/scripts.html#L22-L107)
- [ThemeController.php:482-604](file://vendor/thinkcmf/cmf-api/src/admin/controller/ThemeController.php#L482-L604)

**章节来源**
- [scripts.html:22-107](file://public/themes/admin_default/admin/theme/scripts.html#L22-L107)
- [ThemeController.php:482-604](file://vendor/thinkcmf/cmf-api/src/admin/controller/ThemeController.php#L482-L604)

## 性能考量
- 预览刷新频率
  - 设计器页面存在定时心跳请求，建议在高并发场景下适当降低刷新频率，避免不必要的后端压力。
- 排序提交
  - 排序变更仅在 stop 事件触发，减少频繁网络请求；建议在批量排序结束后再统一提交。
- 缓存清理
  - 保存后执行缓存清理，确保预览能及时反映最新设置。

[本节为通用建议，无需特定文件引用]

## 故障排查指南
- 保存后预览未更新
  - 检查排序/配置提交是否成功返回；确认父级预览器刷新逻辑是否执行。
- 控件排序无效
  - 确认 sortable 初始化与 stop 事件绑定正常；检查聚合的文件ID/区块名/控件ID是否正确。
- 配置表单不显示或报错
  - 检查控件 manifest.json 是否存在且格式正确；确认后端读取与合并逻辑是否覆盖默认值。
- 设备切换后布局异常
  - 检查模拟器类名切换逻辑与样式是否匹配；确认响应式断点设置。

**章节来源**
- [design.html:174-262](file://public/themes/admin_default/admin/theme/design.html#L174-L262)
- [scripts.html:22-107](file://public/themes/admin_default/admin/theme/scripts.html#L22-L107)
- [ThemeController.php:482-604](file://vendor/thinkcmf/cmf-api/src/admin/controller/ThemeController.php#L482-L604)

## 结论
ThinkCMF 的可视化主题设计通过“设计器 + iframe 预览 + 控件清单 + 后端 API + 文件持久化”的组合，实现了所见即所得的主题定制体验。控件系统以 manifest.json 为契约，既保证了灵活性，又确保了配置的可验证与可维护。拖拽排序与实时预览提升了设计效率，而完善的保存与刷新机制保障了前后端一致。

[本节为总结，无需特定文件引用]

## 附录

### A. 主题设计器操作流程（步骤说明）
- 打开设计器：进入主题设计页面，选择目标主题与文件。
- 添加控件：在区块中点击“+”按钮，弹出控件选择框，确认后控件加入区块。
- 拖拽排序：在区块内拖动控件调整顺序；跨区块拖拽可将控件移动到其他区块。
- 配置控件：点击控件设置，填写标题与变量，调整 CSS 外观，点击保存。
- 刷新预览：点击刷新按钮或保存后自动刷新，查看效果。
- 设备切换：支持 PC/PAD/MOBILE 三种设备比例，便于移动端适配。

**章节来源**
- [design.html:147-173](file://public/themes/admin_default/admin/theme/design.html#L147-L173)
- [scripts.html:22-107](file://public/themes/admin_default/admin/theme/scripts.html#L22-L107)
- [file_widget_setting.html:84-106](file://public/themes/admin_default/admin/theme/file_widget_setting.html#L84-L106)

### B. 自定义控件开发指南与最佳实践
- 控件目录结构
  - 在主题 public/widgets/<name>/ 下创建控件目录，提供 manifest.json。
- manifest.json 字段建议
  - title/name/version/demo_url/author/author_url/keywords/description/action
  - vars：定义变量标题、类型、默认值、占位符、提示与验证规则
  - css：定义外观变量（如边距），提供默认值与提示
- 开发建议
  - 变量类型尽量标准化（text/textarea/select/array/rich_text），便于表单渲染与校验
  - 提供合理的默认值与必填规则，降低使用者配置成本
  - 控件命名与目录名保持一致，避免路径解析问题
  - 控件样式尽量使用语义化类名，避免与主题全局样式冲突

**章节来源**
- [manifest.json（特性控件）:12-82](file://public/themes/default/public/widgets/features/manifest.json#L12-L82)
- [manifest.json（文章控件）:1-16](file://public/themes/default/public/widgets/article/manifest.json#L1-L16)

### C. 响应式设计与移动端适配
- 设备模拟
  - 设计器提供 PC/PAD/MOBILE 三种模拟器尺寸与缩放，便于在不同断点下观察布局。
- 建议
  - 控件 CSS 变量优先使用相对单位（em/rem/%），配合媒体查询实现断点适配
  - 控件内部避免使用绝对定位导致在小屏上溢出
  - 使用栅格或弹性布局，提升在不同设备上的可读性与可用性

**章节来源**
- [design.html:74-91](file://public/themes/admin_default/admin/theme/design.html#L74-L91)