Files
fauto-design/draggable-panels/IMPLEMENTATION_PLAN.md
2025-12-21 20:58:34 +08:00

703 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🎯 可视化拖拽设计器 - 整体实施计划
> **目标**将当前的设计器升级为可直接解析和编辑真实Vue文件的可视化拖拽编辑器
> **核心技术**Vue编译时AST转换 + Element Plus布局 + 实时文件修改
> **参考实现**plugins示例项目的拖拽机制
---
## 📋 项目总体架构设计
### **整体技术栈升级**
```
现有架构 → 目标架构
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
设计组件 (虚拟对象) → 真实Vue文件解析
DesignCenter (模拟预览) → 实时渲染的Vue页面
属性编辑 (内存状态) → 直接修改源文件
拖拽排序 (组件实例) → 拖拽修改el-row/el-col布局
```
### **核心功能模块划分**
```
┌─────────────────────────────────────────────────────────┐
│ 可视化拖拽设计器 │
└─────────────────────────────────────────────────────────┘
├─ 模块1: Vue文件解析引擎
│ ├─ AST解析与遍历
│ ├─ 组件树结构提取
│ └─ 元数据生成 (_material路径ID)
├─ 模块2: 编译时增强插件系统
│ ├─ Vite插件配置
│ ├─ Vue模板AST转换
│ ├─ 拖拽指令自动注入
│ └─ 结构化ID生成
├─ 模块3: 运行时拖拽交互系统
│ ├─ 自定义拖拽指令 (v-auto-drag)
│ ├─ 拖拽事件处理 (dragstart/drop/over)
│ ├─ 元素选中与高亮
│ └─ 滚轮切换父子元素
├─ 模块4: 可视化画布系统
│ ├─ 实时渲染Vue页面
│ ├─ el-row/el-col布局展示
│ ├─ 拖拽预览与占位符
│ └─ 组件边界高亮
├─ 模块5: 源文件修改引擎
│ ├─ 模板字符串操作
│ ├─ 元素位置计算与插入
│ ├─ 代码格式化与清理
│ └─ 文件写入API (后端支持)
└─ 模块6: 设计器UI增强
├─ 左侧: 组件树查看器
├─ 中间: 可视化画布
├─ 右侧: 属性编辑器
└─ 顶部: 文件选择与工具栏
```
---
## 🗂️ 项目文件结构设计
```
draggable-panels/
├── src/
│ ├── fauto/
│ │ ├── Designer.vue # 设计器主入口
│ │ │
│ │ ├── components/
│ │ │ ├── Header.vue # ✨新增: 文件选择工具栏
│ │ │ ├── MainLayout.vue # (保持原有)
│ │ │ └── Panel.vue # (保持原有)
│ │ │
│ │ ├── materials/
│ │ │ ├── VueFileExplorer/ # ✨新增: Vue文件浏览器
│ │ │ │ ├── index.vue
│ │ │ │ └── index.json
│ │ │ │
│ │ │ ├── ComponentTreeViewer/ # ✨新增: 组件结构树
│ │ │ │ ├── index.vue
│ │ │ │ └── index.json
│ │ │ │
│ │ │ ├── VisualCanvas/ # ✨新增: 可视化画布
│ │ │ │ ├── index.vue # (实时渲染目标Vue文件)
│ │ │ │ └── index.json
│ │ │ │
│ │ │ ├── PropertyEditor/ # ✨新增: 属性编辑器
│ │ │ │ ├── index.vue
│ │ │ │ └── index.json
│ │ │ │
│ │ │ └── (保留原有物料组件...)
│ │ │
│ │ ├── designComponents/ # (保留,但不再是主要编辑对象)
│ │ │
│ │ ├── core/ # ✨新增: 核心功能模块
│ │ │ ├── parser/ # Vue文件解析器
│ │ │ │ ├── vueParser.ts # SFC解析入口
│ │ │ │ ├── templateParser.ts # template AST解析
│ │ │ │ ├── componentTreeBuilder.ts # 组件树构建
│ │ │ │ └── pathGenerator.ts # 结构化路径生成
│ │ │ │
│ │ │ ├── modifier/ # 源文件修改器
│ │ │ │ ├── templateModifier.ts # 模板修改逻辑
│ │ │ │ ├── elementExtractor.ts # 元素提取与定位
│ │ │ │ ├── codeFormatter.ts # 代码格式化
│ │ │ │ └── fileWriter.ts # 文件写入API封装
│ │ │ │
│ │ │ └── directives/ # 自定义指令
│ │ │ ├── autoDrag.ts # 拖拽指令实现
│ │ │ └── elementSelect.ts # 元素选中指令
│ │ │
│ │ ├── stores/
│ │ │ ├── panelStore.ts # (保留)
│ │ │ ├── designStore.ts # (保留,但角色调整)
│ │ │ │
│ │ │ ├── vueFileStore.ts # ✨新增: Vue文件状态管理
│ │ │ │ # - 当前打开的Vue文件路径
│ │ │ │ # - 解析后的组件树结构
│ │ │ │ # - 当前选中的元素
│ │ │ │ # - 文件缓存与同步
│ │ │ │
│ │ │ └── canvasStore.ts # ✨新增: 画布交互状态
│ │ │ # - 拖拽状态管理
│ │ │ # - 选中元素路径
│ │ │ # - 悬停目标信息
│ │ │
│ │ └── types/
│ │ ├── index.ts # (保留原有)
│ │ ├── vueFile.ts # ✨新增: Vue文件相关类型
│ │ └── canvas.ts # ✨新增: 画布相关类型
│ │
│ └── plugins/ # ✨新增: Vite/Vue插件
│ ├── index.ts # 插件总入口
│ ├── vite/
│ │ └── vueFilePlugin.ts # Vite插件 (文件写入API)
│ └── vue/
│ ├── dragPlugin.ts # Vue编译时插件
│ ├── autoDrag.ts # 拖拽指令实现
│ └── dragStyles.css # 拖拽样式
├── vite.config.ts # ✨修改: 集成新插件
├── package.json # ✨修改: 新增依赖
└── IMPLEMENTATION_PLAN.md # 本文档
```
---
## 📦 阶段性实施计划
### **阶段1: 基础设施搭建** (第1-2天)
#### **任务1.1: 安装依赖与环境准备**
- [ ] 安装Element Plus (`npm install element-plus`)
- [ ] 安装@vue/compiler-sfc (用于AST解析)
- [ ] 配置vite.config.ts引入插件系统
- [ ] 创建测试用的TestPage.vue文件
#### **任务1.2: 创建插件系统框架**
- [ ] 创建 `src/plugins/` 目录结构
- [ ] 实现 `vite/vueFilePlugin.ts` - 文件写入API中间件
- [ ] 实现 `vue/dragPlugin.ts` - Vue编译时节点转换器
- [ ] 配置vite.config.ts加载插件
**验收标准**
- ✅ Vite能正常启动插件加载无报错
- ✅ 访问 `/api/write-file` 能收到响应
- ✅ TestPage.vue编译时能看到插件日志
---
### **阶段2: Vue文件解析引擎** (第3-4天)
#### **任务2.1: 实现SFC解析器**
- [ ] 创建 `core/parser/vueParser.ts`
- 解析Vue单文件组件
- 提取template/script/style
- 缓存原始代码
#### **任务2.2: 实现template AST解析**
- [ ] 创建 `core/parser/templateParser.ts`
- 使用@vue/compiler-dom解析template
- 遍历AST节点
- 识别el-row和el-col元素
#### **任务2.3: 实现组件树构建器**
- [ ] 创建 `core/parser/componentTreeBuilder.ts`
- 递归构建树形结构
- 生成结构化路径ID (r1c2r1c1...)
- 提取组件属性信息
#### **任务2.4: 创建vueFileStore**
- [ ] 创建 `stores/vueFileStore.ts`
- 管理当前打开的Vue文件
- 存储解析后的组件树
- 提供文件加载/保存接口
**验收标准**
- ✅ 能解析TestPage.vue并生成组件树
- ✅ 每个el-row/el-col都有唯一的路径ID
- ✅ vueFileStore能正确缓存解析结果
---
### **阶段3: 编译时增强系统** (第5-6天)
#### **任务3.1: 实现路径ID生成算法**
- [ ] 创建 `core/parser/pathGenerator.ts`
- 实现plugins示例中的路径生成逻辑
- 递归计算父子关系
- 生成r1c2格式的ID
#### **任务3.2: 实现Vue编译时转换器**
- [ ] 完善 `plugins/vue/dragPlugin.ts`
- 在编译时遍历AST
- 自动为el-row/el-col注入v-auto-drag指令
- 注入_material属性
#### **任务3.3: 实现拖拽指令**
- [ ] 创建 `plugins/vue/autoDrag.ts`
- 实现dragstart/dragover/drop事件处理
- 实现元素选中逻辑
- 实现滚轮切换父子元素
- 添加拖拽样式
**验收标准**
- ✅ 编译后的Vue组件自动包含拖拽功能
- ✅ 每个el-col都能被拖拽
- ✅ 点击元素能高亮选中
- ✅ 滚轮能切换选中父子元素
---
### **阶段4: 可视化画布系统** (第7-8天)
#### **任务4.1: 创建VisualCanvas物料组件**
- [ ] 创建 `materials/VisualCanvas/index.vue`
- 动态加载目标Vue文件
- 使用iframe或动态组件渲染
- 应用拖拽样式
#### **任务4.2: 实现画布交互状态管理**
- [ ] 创建 `stores/canvasStore.ts`
- 管理拖拽状态拖拽元素ID、目标位置
- 管理选中状态(当前选中元素路径)
- 提供拖拽预览数据
#### **任务4.3: 实现拖拽视觉反馈**
- [ ] 创建 `plugins/vue/dragStyles.css`
- 拖拽中的半透明效果
- 拖拽进入的高亮边框
- 元素选中的蓝色描边
- 拖拽占位符样式
**验收标准**
- ✅ 画布能正确渲染TestPage.vue
- ✅ 拖拽时有清晰的视觉反馈
- ✅ 选中元素有蓝色描边
- ✅ 悬停目标有红色高亮
---
### **阶段5: 源文件修改引擎** (第9-10天)
#### **任务5.1: 实现元素提取与定位**
- [ ] 创建 `core/modifier/elementExtractor.ts`
- 根据_material ID定位元素
- 提取完整的元素字符串(含子元素)
- 计算元素在模板中的位置
#### **任务5.2: 实现模板修改逻辑**
- [ ] 创建 `core/modifier/templateModifier.ts`
- 移除源元素
- 根据方向插入到目标位置
- 处理嵌套结构的移动
#### **任务5.3: 实现代码格式化与清理**
- [ ] 创建 `core/modifier/codeFormatter.ts`
- 清除运行时注入的_material属性
- 保持原有代码缩进
- 移除多余的空行
#### **任务5.4: 实现文件写入封装**
- [ ] 创建 `core/modifier/fileWriter.ts`
- 调用/api/write-file接口
- 错误处理与重试
- 写入成功后刷新页面
**验收标准**
- ✅ 拖拽后能正确修改Vue源文件
- ✅ 修改后的代码格式正确
- ✅ 不会破坏原有代码结构
- ✅ 页面自动刷新显示修改结果
---
### **阶段6: 设计器UI增强** (第11-12天)
#### **任务6.1: 创建VueFileExplorer**
- [ ] 创建 `materials/VueFileExplorer/index.vue`
- 显示项目中的所有.vue文件
- 点击文件加载到画布
- 显示当前打开的文件路径
#### **任务6.2: 创建ComponentTreeViewer**
- [ ] 创建 `materials/ComponentTreeViewer/index.vue`
- 展示当前Vue文件的组件树
- 显示el-row/el-col的层级结构
- 点击树节点选中画布中的元素
- 支持拖拽调整顺序(可选)
#### **任务6.3: 创建PropertyEditor**
- [ ] 创建 `materials/PropertyEditor/index.vue`
- 显示选中元素的属性span、gutter等
- 支持修改属性值
- 实时更新到源文件
#### **任务6.4: 升级Header工具栏**
- [ ] 修改 `components/Header.vue`
- 添加"打开文件"按钮
- 显示当前文件名
- 添加"保存"、"撤销"按钮(可选)
**验收标准**
- ✅ 能浏览并选择Vue文件
- ✅ 组件树能实时同步画布状态
- ✅ 属性编辑器能修改元素属性
- ✅ 工具栏显示当前操作状态
---
### **阶段7: 功能完善与优化** (第13-14天)
#### **任务7.1: 实现撤销/重做功能**
- [ ] 创建历史记录栈
- [ ] 实现Ctrl+Z撤销
- [ ] 实现Ctrl+Shift+Z重做
#### **任务7.2: 实现拖拽位置预览**
- [ ] 拖拽时显示插入位置占位符
- [ ] 左右方向箭头指示
- [ ] 不合法拖拽的禁止光标
#### **任务7.3: 实现快捷键支持**
- [ ] Delete键删除选中元素
- [ ] Ctrl+C/V 复制粘贴(可选)
- [ ] 方向键微调位置(可选)
#### **任务7.4: 性能优化**
- [ ] 大文件解析性能优化
- [ ] 拖拽事件节流
- [ ] 组件树虚拟滚动(如果元素很多)
**验收标准**
- ✅ 撤销/重做功能正常
- ✅ 拖拽预览清晰直观
- ✅ 快捷键响应流畅
- ✅ 大文件100+元素)操作流畅
---
### **阶段8: 测试与文档** (第15天)
#### **任务8.1: 编写单元测试**
- [ ] 测试Vue文件解析器
- [ ] 测试路径ID生成算法
- [ ] 测试模板修改逻辑
#### **任务8.2: 编写集成测试**
- [ ] 端到端拖拽测试
- [ ] 文件加载与保存测试
- [ ] 多文件切换测试
#### **任务8.3: 更新项目文档**
- [ ] 更新PROJECT_CONTEXT.md
- [ ] 编写用户使用指南
- [ ] 编写开发者文档
---
## 🔧 技术实现关键点
### **1. Vue编译时AST转换**
```typescript
// plugins/vue/dragPlugin.ts
export function dragNodeTransform(node, context) {
// 1. 只处理el-row和el-col元素
if (node.type === 1 && (node.tag === 'el-row' || node.tag === 'el-col')) {
// 2. 生成结构化路径ID
const materialId = generateStructuredPath(node, context)
// 3. 注入v-auto-drag指令
node.props.push({
type: 7, // 指令类型
name: 'auto-drag',
exp: undefined,
modifiers: []
})
// 4. 注入_material属性
node.props.push({
type: 6, // 属性类型
name: '_material',
value: { type: 2, content: materialId }
})
}
}
```
### **2. 结构化路径ID生成算法**
```typescript
// core/parser/pathGenerator.ts
/**
* 生成结构化路径ID
* 例如: r1c2r1 表示 第1个row -> 第2个col -> 第1个row
*/
function generateStructuredPath(node, context) {
// 1. 递归查找父节点路径
const parent = findParentNode(node, context.root)
let parentPath = parent ? generateStructuredPath(parent) : ''
// 2. 计算当前节点在同级中的索引
if (node.tag === 'el-row') {
const rowIndex = calculateSiblingIndex(node, parent, 'el-row')
return `${parentPath}r${rowIndex}`
} else if (node.tag === 'el-col') {
const colIndex = calculateSiblingIndex(node, parent, 'el-col')
return `${parentPath}c${colIndex}`
}
return parentPath
}
```
### **3. 拖拽事件处理流程**
```typescript
// plugins/vue/autoDrag.ts
// 拖拽开始
function onDragStart(e, elementId) {
__dragElement.value = elementId
e.target.classList.add('drag-overlay')
}
// 拖拽悬停
function onDragOver(e, targetId) {
const rect = e.target.getBoundingClientRect()
const direction = e.clientX - rect.left < rect.width / 2 ? 'left' : 'right'
__dropInfo.value = { targetId, direction }
e.target.classList.add('drag-enter')
}
// 拖拽放置
function onDrop(e, targetId) {
// 调用模板修改器
modifyVueTemplate(__dragElement.value, targetId, __dropInfo.value.direction)
// 清理状态
cleanupDragState()
}
```
### **4. 模板字符串修改逻辑**
```typescript
// core/modifier/templateModifier.ts
function modifyTemplate(sourceId, targetId, direction) {
// 1. 提取源元素
const sourceElement = extractElementById(template, sourceId)
// 2. 从模板中移除源元素
template = template.replace(sourceElement, '')
// 3. 查找目标元素位置
const targetPosition = findElementPosition(template, targetId)
// 4. 根据方向插入源元素
if (direction === 'left') {
template = insertBefore(template, targetPosition, sourceElement)
} else {
template = insertAfter(template, targetPosition, sourceElement)
}
// 5. 清理_material属性
template = template.replace(/\s+_material="[^"]*"/g, '')
return template
}
```
### **5. 文件写入API**
```typescript
// plugins/vite/vueFilePlugin.ts
configureServer(server) {
server.middlewares.use('/api/write-file', async (req, res) => {
const { filePath, content } = await parseBody(req)
const fullPath = path.resolve(process.cwd(), filePath)
fs.writeFileSync(fullPath, content, 'utf8')
res.end(JSON.stringify({ success: true }))
})
}
```
---
## 🎯 核心数据流
```
1. 用户打开Vue文件
2. vueFileStore.loadFile(path)
3. vueParser解析SFC → 提取template
4. templateParser解析AST → 构建组件树
5. 组件树显示在ComponentTreeViewer
6. VisualCanvas渲染Vue文件编译时已注入拖拽指令
7. 用户在画布中拖拽el-col
8. dragStart → 记录源ID
9. dragOver → 计算目标ID和方向
10. drop → 调用templateModifier
11. 修改template字符串
12. 调用/api/write-file写入文件
13. Vite热更新 → 页面自动刷新
14. 显示修改后的布局 ✅
```
---
## 📊 关键类型定义
```typescript
// types/vueFile.ts
export interface VueFileInfo {
path: string // 文件路径
name: string // 文件名
content: string // 原始内容
template: string // 模板部分
componentTree: ComponentNode[] // 组件树
lastModified: number // 最后修改时间
}
export interface ComponentNode {
id: string // 结构化路径ID (r1c2)
tag: 'el-row' | 'el-col' // 标签类型
attrs: Record<string, any> // 属性 (span, gutter等)
children: ComponentNode[] // 子节点
parent: ComponentNode | null // 父节点引用
depth: number // 深度(用于树形展示)
}
// types/canvas.ts
export interface DragState {
dragElementId: string | null // 正在拖拽的元素ID
targetElementId: string | null // 目标元素ID
direction: 'left' | 'right' | null // 插入方向
isDragging: boolean // 是否正在拖拽
}
export interface SelectState {
selectedElementId: string | null // 选中的元素ID
selectedElementPath: ComponentNode[] // 选中元素的父级路径
hoverElementId: string | null // 鼠标悬停的元素ID
}
```
---
## ⚠️ 技术难点与解决方案
### **难点1: AST节点父级查找**
**问题**Vue编译时AST节点没有parent引用
**解决**:递归遍历根节点,构建节点关系映射表
### **难点2: 字符串操作的准确性**
**问题**:正则匹配可能误删嵌套元素
**解决**:使用深度计数器,精确匹配开始/结束标签
### **难点3: 热更新与拖拽状态冲突**
**问题**:文件修改后页面刷新,拖拽状态丢失
**解决**将拖拽状态保存到sessionStorage刷新后恢复
### **难点4: 大文件性能问题**
**问题**100+元素的Vue文件解析缓慢
**解决**使用Web Worker后台解析分片渲染组件树
### **难点5: 跨iframe通信**
**问题**如果画布使用iframe事件监听复杂
**解决**使用动态组件代替iframe或通过postMessage通信
---
## 🚀 启动指南(实施后)
```bash
# 1. 安装依赖
npm install
# 2. 启动开发服务器
npm run dev
# 3. 访问设计器
http://localhost:5173/draggable
# 4. 操作流程
- 点击"窗口" → 打开"Vue文件浏览器"(左侧)
- 点击"窗口" → 打开"可视化画布"(中间)
- 点击"窗口" → 打开"组件树查看器"(左侧)
- 在文件浏览器中选择TestPage.vue
- 在画布中拖拽el-col调整布局
- 查看源文件自动更新
```
---
## 📝 后续可扩展功能
### **功能增强**
1. **组件库集成**
- 支持从Element Plus组件库拖拽添加组件
- 不仅限于el-row/el-col支持所有组件
2. **多文件项目管理**
- 项目文件树浏览
- 多文件同时编辑Tab切换
- 文件依赖关系分析
3. **智能布局**
- 自动计算span总和
- 响应式布局预览xs/sm/md/lg
- 对齐辅助线
4. **团队协作**
- Git集成提交/拉取)
- 实时协作编辑WebSocket
- 版本历史对比
### **开发体验**
1. **代码生成**
- 自动生成配套的script代码
- 自动导入组件
- 自动生成数据绑定
2. **预设模板**
- 常用布局模板库
- 一键应用模板
- 自定义模板保存
---
## ✅ 验收清单(最终)
- [ ] 能浏览并选择项目中的.vue文件
- [ ] 能在画布中实时渲染Vue文件
- [ ] 能通过拖拽调整el-row/el-col布局
- [ ] 拖拽操作能正确修改源文件
- [ ] 文件修改后页面自动刷新
- [ ] 点击元素能高亮选中
- [ ] 滚轮能切换父子元素选中
- [ ] 组件树能实时同步画布状态
- [ ] 属性编辑器能修改元素属性
- [ ] 支持撤销/重做操作
- [ ] 所有操作流畅无明显卡顿
- [ ] 代码格式保持规范
---
## 🎉 总结
本计划将分**8个阶段、15天**完成,核心思路是:
1. **编译时增强**通过Vue编译器插件自动注入拖拽能力
2. **运行时交互**:通过自定义指令实现拖拽逻辑
3. **字符串操作**:精确修改模板字符串
4. **文件同步**通过Vite中间件实现文件写入
相比于传统的虚拟组件设计器,这种方案的优势是:
-**所见即所得**直接编辑真实的Vue文件
-**零学习成本**开发者无需学习DSL或JSON配置
-**完全可控**:生成的代码就是手写的代码
-**灵活扩展**可以支持任意Vue组件
**让我们开始实施吧!** 🚀