424 lines
12 KiB
Markdown
424 lines
12 KiB
Markdown
# Vue页面可视化设计器 - 项目设计文档
|
||
|
||
## 1. 项目概述
|
||
|
||
本项目是一个基于 **Vue3 + TypeScript + Element Plus** 的可视化页面设计器,通过拖拽操作直接编辑真实的Vue源文件,实现低代码页面快速构建。
|
||
|
||
### 1.1 核心特性
|
||
|
||
| 特性 | 描述 |
|
||
|------|------|
|
||
| 直接解析Vue文件 | 动态渲染真实的.vue页面 |
|
||
| 源码级修改 | 拖拽操作直接修改Vue源文件 |
|
||
| 智能层级选择 | 键盘方向键切换嵌套元素层级 |
|
||
| 元数据编辑 | 可视化编辑组件属性 |
|
||
| 多视图联动 | 设计中心、结构树、元数据面板同步选中 |
|
||
| 热更新 | 修改后自动刷新预览 |
|
||
|
||
### 1.2 技术架构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ 前端应用 (Vue3 + Vite) │
|
||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||
│ │ 页面管理 │ │设计组件列│ │ 设计中心 │ │ 元数据 │ │
|
||
│ │ │ │表 │ │ │ │ 编辑器 │ │
|
||
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
||
│ │
|
||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||
│ │ Pinia 状态管理 + 插件系统 │ │
|
||
│ │ dragStore │ interactionStore │ designStore │ vueFileStore│ │
|
||
│ └──────────────────────────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
│ HTTP API
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ 后端服务 (Node.js + Express) │
|
||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||
│ │ templateService - Vue模板AST解析与修改 │ │
|
||
│ │ @vue/compiler-sfc + @vue/compiler-dom │ │
|
||
│ └──────────────────────────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
│ 文件操作
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ Vue源文件 (src/views/*.vue) │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 核心模块设计
|
||
|
||
### 2.1 前端模块
|
||
|
||
#### 2.1.1 设计中心 (DesignCenter)
|
||
|
||
**职责**:动态渲染选中的Vue页面,注入交互事件
|
||
|
||
**核心组件**:
|
||
- `index.vue` - 主容器,动态加载Vue页面组件
|
||
- `InteractiveWrapper.vue` - 交互包装器,注入事件监听
|
||
- `DropZone.vue` - 拖放区域指示器
|
||
- `DragPreview.vue` - 拖拽跟随预览
|
||
|
||
**交互事件注入流程**:
|
||
```typescript
|
||
// 1. 动态渲染页面组件
|
||
<component :is="selectedPageComponent" />
|
||
|
||
// 2. 挂载后扫描DOM,注入事件
|
||
const injectInteractionEvents = () => {
|
||
document.querySelectorAll('.el-row, .el-col').forEach(el => {
|
||
const path = generateElementPath(el) // 生成结构化路径
|
||
bindElementEvents(el, type, path) // 绑定交互事件
|
||
})
|
||
}
|
||
|
||
// 3. MutationObserver监听DOM变化,动态注入
|
||
observer.observe(container, { childList: true, subtree: true })
|
||
```
|
||
|
||
#### 2.1.2 结构树 (TreeViewer)
|
||
|
||
**职责**:展示页面的el-row/el-col结构,支持选中和删除
|
||
|
||
**功能**:
|
||
- 树形展示页面结构
|
||
- 点击节点选中对应元素
|
||
- 拖拽节点调整顺序
|
||
- 右键菜单/删除图标/Del键删除
|
||
|
||
#### 2.1.3 元数据编辑器 (DataTable)
|
||
|
||
**职责**:展示和编辑选中组件的属性
|
||
|
||
**支持的属性类型**:
|
||
- `number` - 数字输入(如span宽度)
|
||
- `text` - 文本输入(如placeholder)
|
||
- `select` - 下拉选择(如size尺寸)
|
||
- `boolean` - 开关切换(如stripe斑马纹)
|
||
- `color` - 颜色选择器
|
||
|
||
#### 2.1.4 插件系统 (plugins/)
|
||
|
||
**dragStore** - 拖拽状态管理
|
||
```typescript
|
||
interface DragStore {
|
||
// 状态
|
||
isDragging: boolean
|
||
dragPhase: 'source' | 'target' // 两阶段拖拽
|
||
dragSource: DragSource | null
|
||
hierarchyNodes: HierarchyNode[] // 层级节点列表
|
||
selectedHierarchyIndex: number // 当前选中层级
|
||
hoverDirection: Direction | null
|
||
|
||
// 方法
|
||
startDragFromCanvas(path, type, element) // 开始画布内拖拽
|
||
startDragFromComponentList(id, name, template) // 开始组件列表拖拽
|
||
enterTargetPhase(element) // 进入目标选择阶段
|
||
selectParentLevel() // 选择父级(↑键)
|
||
selectChildLevel() // 选择子级(↓键)
|
||
confirmDrop(pagePath) // 确认拖放
|
||
}
|
||
```
|
||
|
||
**interactionStore** - 交互事件钩子
|
||
```typescript
|
||
interface InteractionStore {
|
||
hoverTarget: InteractionTarget | null
|
||
selectedTarget: InteractionTarget | null
|
||
|
||
onHover(target)
|
||
onClick(target)
|
||
onLeave()
|
||
}
|
||
```
|
||
|
||
### 2.2 后端模块
|
||
|
||
#### 2.2.1 templateService
|
||
|
||
**职责**:解析Vue文件,执行结构修改
|
||
|
||
**核心函数**:
|
||
|
||
| 函数 | 功能 |
|
||
|------|------|
|
||
| `moveElement(vueContent, options)` | 移动元素到新位置 |
|
||
| `insertElement(vueContent, options)` | 插入新元素 |
|
||
| `deleteElement(vueContent, elementPath)` | 删除元素 |
|
||
| `parseElementTree(vueContent)` | 解析页面结构树 |
|
||
| `parseComponentProps(vueContent, path)` | 获取组件属性 |
|
||
| `updateComponentProps(vueContent, path, updates)` | 更新组件属性 |
|
||
|
||
**技术实现**:
|
||
```javascript
|
||
// 使用 @vue/compiler-sfc 解析Vue文件
|
||
import { parse as parseSFC } from '@vue/compiler-sfc'
|
||
// 使用 @vue/compiler-dom 解析template获取AST
|
||
import { parse as parseTemplate } from '@vue/compiler-dom'
|
||
|
||
// 通过AST定位元素,使用字符串操作修改(保持原始格式)
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 数据结构设计
|
||
|
||
### 3.1 结构化路径
|
||
|
||
**格式**:`r{n}c{m}r{x}c{y}...`
|
||
|
||
| 前缀 | 含义 |
|
||
|------|------|
|
||
| r | el-row |
|
||
| c | el-col |
|
||
| 数字 | 同级元素中的索引(从1开始) |
|
||
|
||
**示例**:`r1c2r1` = 第1个row → 第2个col → 第1个row
|
||
|
||
### 3.2 设计组件配置
|
||
|
||
```json
|
||
{
|
||
"id": "TextInput",
|
||
"name": "输入框",
|
||
"icon": "✏️",
|
||
"description": "用于输入文本内容的表单组件",
|
||
"defaultSpan": 12,
|
||
"metadata": {
|
||
"span": {
|
||
"label": "宽度",
|
||
"type": "number",
|
||
"min": 1,
|
||
"max": 24,
|
||
"target": "el-col",
|
||
"attr": ":span"
|
||
},
|
||
"placeholder": {
|
||
"label": "占位符",
|
||
"type": "text",
|
||
"target": "el-input",
|
||
"attr": "placeholder"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3.3 设计组件模板
|
||
|
||
```html
|
||
<el-col :span="12">
|
||
<div class="design-component design-text-input" data-component="输入框">
|
||
<el-form-item label="文本输入">
|
||
<el-input v-model="inputValue" placeholder="请输入内容"></el-input>
|
||
</el-form-item>
|
||
</div>
|
||
</el-col>
|
||
```
|
||
|
||
**关键点**:
|
||
- 外层必须是 `el-col`
|
||
- 内层 `div` 必须有 `data-component` 属性标识组件类型
|
||
- 使用 `class="design-component"` 标识设计组件
|
||
|
||
---
|
||
|
||
## 4. API设计
|
||
|
||
### 4.1 元素操作API
|
||
|
||
#### 移动元素
|
||
```
|
||
POST /api/move-element
|
||
Body: {
|
||
"pagePath": "D:/workspace/.../TestPage1.vue",
|
||
"source": {
|
||
"type": "canvas-element",
|
||
"path": "r1c1",
|
||
"elementType": "ec"
|
||
},
|
||
"targetPath": "r1c2",
|
||
"targetType": "ec",
|
||
"direction": "right" // top|bottom|left|right|inside
|
||
}
|
||
```
|
||
|
||
#### 插入元素
|
||
```
|
||
POST /api/insert-element
|
||
Body: {
|
||
"pagePath": "...",
|
||
"templateContent": "<el-col :span=\"12\">...</el-col>",
|
||
"targetPath": "r1c1",
|
||
"direction": "right"
|
||
}
|
||
```
|
||
|
||
#### 删除元素
|
||
```
|
||
POST /api/delete-element
|
||
Body: {
|
||
"pagePath": "...",
|
||
"elementPath": "r1c2"
|
||
}
|
||
```
|
||
|
||
### 4.2 属性操作API
|
||
|
||
#### 获取组件属性
|
||
```
|
||
GET /api/component-props?pagePath=...&elementPath=r1c1
|
||
Response: {
|
||
"success": true,
|
||
"componentId": "输入框",
|
||
"props": {
|
||
"span": 12,
|
||
"el-input:placeholder": "请输入"
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 更新组件属性
|
||
```
|
||
POST /api/update-props
|
||
Body: {
|
||
"pagePath": "...",
|
||
"elementPath": "r1c1",
|
||
"updates": {
|
||
"span": 8,
|
||
"el-input:placeholder": "新占位符"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4.3 结构查询API
|
||
|
||
#### 获取页面结构树
|
||
```
|
||
GET /api/element-tree?pagePath=...
|
||
Response: {
|
||
"success": true,
|
||
"tree": [
|
||
{
|
||
"type": "row",
|
||
"path": "r1",
|
||
"label": "el-row",
|
||
"children": [
|
||
{
|
||
"type": "col",
|
||
"path": "r1c1",
|
||
"componentName": "输入框",
|
||
"children": []
|
||
}
|
||
]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 交互设计
|
||
|
||
### 5.1 拖拽流程
|
||
|
||
```
|
||
1. 用户开始拖拽(组件列表/画布元素)
|
||
↓
|
||
2. 进入源选择阶段,收集层级节点
|
||
↓
|
||
3. 移动到目标位置,进入目标选择阶段
|
||
↓
|
||
4. 显示DropZone(上/下/左/右/放入)
|
||
↓
|
||
5. 用户点击方向确认拖放
|
||
↓
|
||
6. 调用后端API修改源文件
|
||
↓
|
||
7. 触发vue-template-updated事件
|
||
↓
|
||
8. 前端刷新页面和结构树
|
||
```
|
||
|
||
### 5.2 层级选择
|
||
|
||
当鼠标悬停在嵌套元素上时(如`r1c1r1c1`):
|
||
|
||
- **默认选中**:最深层级(r1c1r1c1)
|
||
- **↑键**:切换到父级(r1c1r1 → r1c1 → r1)
|
||
- **↓键**:切换到子级
|
||
- **Esc**:取消拖拽
|
||
|
||
### 5.3 多视图联动
|
||
|
||
- 点击**设计中心**元素 → 结构树高亮 + 元数据面板更新
|
||
- 点击**结构树**节点 → 设计中心高亮 + 元数据面板更新
|
||
- **元数据面板**修改 → 源文件更新 → 设计中心刷新
|
||
|
||
---
|
||
|
||
## 6. 扩展指南
|
||
|
||
### 6.1 添加设计组件
|
||
|
||
1. 在 `src/fauto/designComponents/` 创建目录
|
||
2. 添加 `index.json`(配置+元数据schema)
|
||
3. 添加 `template.html`(组件模板)
|
||
4. 自动在组件列表显示
|
||
|
||
### 6.2 添加物料组件
|
||
|
||
1. 在 `src/fauto/materials/` 创建目录
|
||
2. 添加 `index.vue`(组件实现)
|
||
3. 添加 `index.json`(配置信息)
|
||
4. 在 `materials/index.ts` 注册
|
||
|
||
### 6.3 扩展元数据类型
|
||
|
||
在 `designStore.ts` 的 `MetadataField` 接口添加新类型,并在 `DataTable/index.vue` 添加对应的编辑控件。
|
||
|
||
---
|
||
|
||
## 7. 技术规范
|
||
|
||
### 7.1 Vue页面规范
|
||
|
||
```vue
|
||
<template>
|
||
<!-- 第一层级必须且只能有一个el-row -->
|
||
<el-row :gutter="20">
|
||
<el-col :span="24">
|
||
<!-- 设计组件必须有data-component属性 -->
|
||
<div class="design-component" data-component="组件名">
|
||
<!-- 组件内容 -->
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
</template>
|
||
```
|
||
|
||
### 7.2 代码规范
|
||
|
||
- TypeScript 严格模式
|
||
- Composition API 组织代码
|
||
- Props 必须定义类型
|
||
- 样式使用 scoped
|
||
- 插件代码放 `fauto/plugins/`
|
||
|
||
---
|
||
|
||
## 8. 版本信息
|
||
|
||
- **前端框架**: Vue 3.5.24
|
||
- **构建工具**: Vite 7.3.0
|
||
- **状态管理**: Pinia 3.0.4
|
||
- **UI框架**: Element Plus
|
||
- **后端运行时**: Node.js
|
||
- **模板解析**: @vue/compiler-sfc, @vue/compiler-dom
|
||
|
||
---
|
||
|
||
**文档更新时间**:2026-01-20
|