diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 0000000..61dc321 --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,597 @@ +# Fauto Design - 开发指南 + +本文档为开发者提供项目的开发规范、扩展指南和最佳实践。 + +--- + +## 目录 + +1. [开发环境配置](#1-开发环境配置) +2. [代码规范](#2-代码规范) +3. [Vue页面规范](#3-vue页面规范) +4. [设计组件开发](#4-设计组件开发) +5. [物料组件开发](#5-物料组件开发) +6. [插件开发](#6-插件开发) +7. [后端服务扩展](#7-后端服务扩展) +8. [常见问题与经验](#8-常见问题与经验) + +--- + +## 1. 开发环境配置 + +### 1.1 前端环境 + +```bash +cd draggable-panels +npm install +npm run dev +``` + +开发服务器:`http://localhost:5173` +设计器入口:`http://localhost:5173/draggable` + +### 1.2 后端环境 + +```bash +cd vue-template-service +npm install +node src/index.js +``` + +API服务:`http://localhost:3001` + +### 1.3 推荐IDE配置 + +- VS Code + Volar 扩展 +- 启用 TypeScript 严格模式 +- 配置 ESLint + Prettier + +--- + +## 2. 代码规范 + +### 2.1 TypeScript规范 + +```typescript +// ✅ 正确:明确定义类型 +interface UserInfo { + id: string + name: string + age: number +} + +const user: UserInfo = { id: '1', name: 'Tom', age: 18 } + +// ❌ 错误:使用any +const user: any = { ... } +``` + +### 2.2 Vue组件规范 + +```vue + + + + + +``` + +### 2.3 文件命名规范 + +| 类型 | 规范 | 示例 | +|------|------|------| +| Vue组件 | PascalCase | `UserProfile.vue` | +| TypeScript文件 | camelCase | `dragStore.ts` | +| 目录 | camelCase/PascalCase | `designComponents/` | +| JSON配置 | camelCase | `index.json` | + +### 2.4 目录职责 + +``` +draggable-panels/src/fauto/ +├── components/ # 基础UI组件(布局相关) +├── materials/ # 物料组件(可拖拽到面板的功能组件) +├── designComponents/ # 设计组件(可拖拽到页面的业务组件) +├── plugins/ # 插件系统(交互、拖拽、路径等核心逻辑) +├── stores/ # Pinia状态管理 +└── types/ # 类型定义 +``` + +--- + +## 3. Vue页面规范 + +### 3.1 结构要求 + +**强制规则**:template第一层级**有且仅有一个el-row** + +```vue + +``` + +```vue + +``` + +### 3.2 设计组件标识 + +页面中的设计组件需要 `data-component` 属性: + +```html + +
+ + + +
+
+``` + +**关键点**: +- `data-component` 的值必须与设计组件的 `name` 一致 +- 用于元数据面板识别组件类型并加载对应的属性schema + +### 3.3 结构化路径 + +系统会自动为el-row/el-col生成路径ID: + +| 路径 | 含义 | +|------|------| +| `r1` | 第1个el-row | +| `r1c2` | 第1个el-row的第2个el-col | +| `r1c2r1` | 第1个row → 第2个col → 第1个row | +| `r1c2r1c3` | 更深层嵌套... | + +--- + +## 4. 设计组件开发 + +### 4.1 创建步骤 + +1. 在 `draggable-panels/src/fauto/designComponents/` 创建目录 +2. 添加 `index.json` 配置文件 +3. 添加 `template.html` 模板文件 + +### 4.2 配置文件 (index.json) + +```json +{ + "id": "MyInput", + "name": "我的输入框", + "icon": "✏️", + "description": "自定义输入组件", + "defaultSpan": 12, + "metadata": { + "span": { + "label": "宽度", + "type": "number", + "min": 1, + "max": 24, + "target": "el-col", + "attr": ":span" + }, + "label": { + "label": "标签", + "type": "text", + "target": "el-form-item", + "attr": "label" + }, + "placeholder": { + "label": "占位符", + "type": "text", + "target": "el-input", + "attr": "placeholder" + }, + "disabled": { + "label": "禁用", + "type": "boolean", + "target": "el-input", + "attr": "disabled" + }, + "size": { + "label": "尺寸", + "type": "select", + "options": ["", "large", "default", "small"], + "target": "el-input", + "attr": "size" + }, + "textColor": { + "label": "文字颜色", + "type": "color", + "target": "el-input", + "attr": "text-color" + } + } +} +``` + +### 4.3 元数据类型 + +| type | 说明 | 渲染控件 | +|------|------|----------| +| `number` | 数字 | 数字输入框 | +| `text` | 文本 | 文本输入框 | +| `select` | 选择 | 下拉选择框 | +| `boolean` | 布尔 | 开关/复选框 | +| `color` | 颜色 | 颜色选择器 | +| `columns` | 列配置 | (暂不支持) | + +### 4.4 模板文件 (template.html) + +```html + +
+ + + +
+
+``` + +**模板规范**: +- 外层必须是 `el-col` +- 内层 `div` 必须有 `class="design-component"` 和 `data-component` +- `data-component` 值与 `index.json` 的 `name` 一致 + +### 4.5 扩展元数据类型 + +如需添加新的元数据类型: + +1. 在 `designStore.ts` 的 `MetadataField.type` 添加类型 +2. 在 `DataTable/index.vue` 添加对应的编辑控件 + +```typescript +// designStore.ts +export interface MetadataField { + type: 'text' | 'number' | 'select' | 'boolean' | 'color' | 'myNewType' + // ... +} +``` + +```vue + + +``` + +--- + +## 5. 物料组件开发 + +### 5.1 创建步骤 + +1. 在 `draggable-panels/src/fauto/materials/` 创建目录 +2. 添加 `index.vue` 组件文件 +3. 添加 `index.json` 配置文件 +4. 在 `materials/index.ts` 注册 + +### 5.2 组件文件 (index.vue) + +```vue + + + + + +``` + +### 5.3 配置文件 (index.json) + +```json +{ + "id": "MyMaterial", + "name": "我的物料", + "icon": "🔧", + "description": "物料组件描述", + "category": "tool", + "defaultPanel": "left" +} +``` + +### 5.4 注册物料 + +```typescript +// materials/index.ts +export { default as MyMaterial } from './MyMaterial/index.vue' + +export const materialConfigs = { + // ... + MyMaterial: () => import('./MyMaterial/index.json') +} +``` + +--- + +## 6. 插件开发 + +### 6.1 插件位置 + +所有与交互、拖拽、路径相关的核心逻辑放在 `draggable-panels/src/fauto/plugins/` + +### 6.2 创建新插件 + +```typescript +// plugins/myPlugin.ts +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' + +export const useMyPlugin = defineStore('myPlugin', () => { + // 状态 + const state = ref(initialState) + + // 计算属性 + const derivedValue = computed(() => ...) + + // 方法 + const doSomething = () => { ... } + + return { + state, + derivedValue, + doSomething + } +}) +``` + +### 6.3 统一导出 + +```typescript +// plugins/index.ts +export { useMyPlugin } from './myPlugin' +``` + +--- + +## 7. 后端服务扩展 + +### 7.1 添加新API + +```javascript +// vue-template-service/src/index.js +app.post('/api/my-endpoint', async (req, res) => { + try { + const { param1, param2 } = req.body + + // 业务逻辑 + const result = await myService(param1, param2) + + res.json({ success: true, data: result }) + } catch (error) { + console.error('[API] 错误:', error) + res.status(500).json({ success: false, error: error.message }) + } +}) +``` + +### 7.2 添加新服务函数 + +```javascript +// vue-template-service/src/services/templateService.js +export function myNewFunction(vueContent, options) { + try { + // 1. 解析Vue文件 + const sfcResult = parseSFC(vueContent) + const templateBlock = sfcResult.descriptor.template + + // 2. 解析template AST + const ast = parseTemplate(templateBlock.content, { + comments: true, + whitespace: 'preserve' + }) + + // 3. 处理逻辑 + // ... + + return { success: true, data: ... } + } catch (error) { + return { success: false, error: error.message } + } +} +``` + +--- + +## 8. 常见问题与经验 + +### 8.1 拖拽相关 + +#### 两阶段拖拽 + +拖拽分为源选择和目标选择两个阶段,避免误操作: + +```typescript +// dragStore.ts +dragPhase: 'source' | 'target' + +// 源选择阶段:收集层级节点 +// 目标选择阶段:选择放置位置 +``` + +#### 层级选择持久化 + +在目标选择阶段切换层级时,需要记住之前的选择: + +```typescript +// 当移动到新元素时,尝试保持相同的层级深度 +if (previousSelectedIndex !== null) { + selectedHierarchyIndex = Math.min(previousSelectedIndex, nodes.length - 1) +} +``` + +### 8.2 交互相关 + +#### 屏蔽浏览器右键菜单 + +实现自定义右键菜单时必须阻止默认行为: + +```typescript +const handleContextMenu = (e: MouseEvent) => { + e.preventDefault() + e.stopPropagation() + // 显示自定义菜单 +} +``` + +#### 多视图联动 + +设计中心、结构树、元数据面板需要同步选中状态: + +```typescript +// 使用自定义事件 +window.dispatchEvent(new CustomEvent('design-component-selected', { + detail: { path, componentName } +})) + +// 其他组件监听 +window.addEventListener('design-component-selected', handler) +``` + +### 8.3 后端相关 + +#### 保持代码格式 + +修改Vue文件时使用字符串操作而非AST重建,保持原始格式: + +```javascript +// ✅ 正确:字符串替换 +const newContent = content.substring(0, start) + newCode + content.substring(end) + +// ❌ 避免:AST重建会丢失格式 +const newAST = transform(ast) +const newContent = generate(newAST) +``` + +#### 热更新触发 + +修改Vue文件后触发事件让前端刷新: + +```typescript +window.dispatchEvent(new CustomEvent('vue-template-updated', { + detail: { pagePath } +})) +``` + +### 8.4 性能优化 + +#### 防止重复绑定 + +使用标记属性避免重复绑定事件: + +```typescript +if (element.hasAttribute('data-fauto-bindend')) return +element.setAttribute('data-fauto-bindend', 'true') +``` + +#### MutationObserver + +监听DOM变化动态注入事件: + +```typescript +const observer = new MutationObserver((mutations) => { + // 检查是否有新的el-row/el-col + if (hasNewElements) { + injectInteractionEvents() + } +}) +observer.observe(container, { childList: true, subtree: true }) +``` + +--- + +## 9. 调试技巧 + +### 9.1 前端调试 + +```typescript +// 在关键位置添加日志 +console.log('[模块名] 操作:', { data }) + +// 使用Vue DevTools查看Pinia状态 +// 使用浏览器DevTools的Elements面板查看data-path属性 +``` + +### 9.2 后端调试 + +```javascript +// 添加详细日志 +console.log(`[API] 请求: ${req.path}`, req.body) +console.log(`[Service] 处理结果:`, result) +``` + +### 9.3 常用调试命令 + +```bash +# 查看端口占用 +netstat -ano | findstr :3001 + +# 杀死进程 +taskkill /F /PID +``` + +--- + +**文档更新时间**:2026-01-20 diff --git a/README.md b/README.md index ede3dc0..44b859a 100644 --- a/README.md +++ b/README.md @@ -1,166 +1,192 @@ # Fauto Design - Vue页面可视化设计器

- Vue 3 - TypeScript - Vite - Element Plus - Node.js + Vue 3.5 + TypeScript + Vite + Element Plus

-

- 🤖 本项目完全由 Qoder AI 开发,所有代码均为 AI 生成 -

+## 📖 简介 ---- +**Fauto Design** 是一个基于 Vue3 的可视化页面设计器,通过拖拽操作直接编辑真实的Vue源文件,实现**所见即所得**的低代码开发体验。 -## 📖 项目简介 +### ✨ 特性 -**Fauto Design** 是一个基于 Vue3 的**可视化页面设计器**,通过拖拽操作直接编辑真实的 Vue 源文件。与传统低代码平台不同,本项目直接操作 `.vue` 文件,生成的代码即是最终代码。 - -### ✨ 核心特性 - -- 🎯 **直接编辑源码** - 拖拽操作实时修改 Vue 文件,所见即所得 -- 🖱️ **智能拖拽交互** - 两阶段拖拽:先选源元素层级,再选目标位置 -- ⌨️ **键盘层级选择** - ↑↓ 键切换嵌套元素层级,精准定位 -- 🔄 **跨类型拖放** - 支持 el-row/el-col 之间的灵活布局调整 -- 📝 **保持代码格式** - AST 解析 + 智能缩进,保持源码整洁 - ---- - -## 🏗️ 技术架构 - -``` -┌─────────────────────────────────────────────────┐ -│ 前端 (Vue3 + Vite) │ -│ ┌─────────────┐ ┌─────────────┐ ┌─────────┐ │ -│ │ PageManager │ │DesignCenter│ │DropZone │ │ -│ └─────────────┘ └─────────────┘ └─────────┘ │ -│ ↓ ↓ ↓ │ -│ ┌──────────────────────────────────────────┐ │ -│ │ dragStore (Pinia) │ │ -│ │ 两阶段拖拽 | 层级选择 | 跨类型判断 │ │ -│ └──────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────┘ - │ HTTP - ▼ -┌─────────────────────────────────────────────────┐ -│ 后端 (Node.js + Express) │ -│ ┌──────────────────────────────────────────┐ │ -│ │ templateService.js │ │ -│ │ @vue/compiler-sfc | AST解析 | 元素移动 │ │ -│ └──────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────┘ - │ - ▼ - Vue 源文件 (*.vue) -``` - ---- +- 🎯 **源码级编辑** - 拖拽操作直接修改Vue文件,支持热更新 +- 🔧 **非侵入式设计** - 无需修改业务页面代码即可实现可视化编辑 +- 🎨 **智能层级选择** - 键盘方向键精准切换嵌套元素层级 +- 📝 **元数据编辑** - 可视化修改组件属性(宽度、颜色、尺寸等) +- 🌳 **结构树视图** - 清晰展示页面el-row/el-col结构 +- 🔄 **多视图联动** - 设计中心、结构树、属性面板实时同步 ## 🚀 快速开始 ### 环境要求 -- Node.js >= 18.x -- npm >= 9.x +- Node.js >= 18 +- npm >= 9 -### 安装依赖 +### 安装与运行 ```bash -# 前端 +# 克隆项目 +git clone +cd fauto-design + +# 启动前端 cd draggable-panels npm install - -# 后端 -cd vue-template-service -npm install -``` - -### 启动服务 - -```bash -# 终端1 - 启动前端 (端口 5173) -cd draggable-panels npm run dev -# 终端2 - 启动后端 (端口 3001) +# 新开终端,启动后端 cd vue-template-service +npm install node src/index.js ``` -### 访问 +### 访问设计器 -打开浏览器访问:**http://localhost:5173/draggable** +``` +http://localhost:5173/draggable +``` ---- +## 📸 功能演示 + +### 基本操作流程 + +1. **选择页面** - 在左侧"页面管理"中选择要编辑的Vue文件 +2. **拖拽组件** - 从"设计组件列表"拖拽组件到设计区域 +3. **调整位置** - 使用↑↓键切换层级,选择放置方向 +4. **编辑属性** - 点击组件,在"元数据"面板编辑属性 +5. **删除元素** - 右键菜单、Del键或点击删除图标 + +### 键盘快捷键 + +| 快捷键 | 功能 | +|--------|------| +| ↑ | 选择父级元素 | +| ↓ | 选择子级元素 | +| Delete | 删除选中元素 | +| Esc | 取消拖拽操作 | ## 📁 项目结构 ``` fauto-design/ -├── 项目上下文.md # AI 协作上下文文档 -├── 项目设计文档.md # 详细技术设计文档 -├── README.md # 本文件 -│ ├── draggable-panels/ # 前端项目 -│ └── src/ -│ ├── fauto/ # 设计器核心 -│ │ ├── components/ # UI组件 -│ │ ├── materials/ # 物料组件 -│ │ ├── plugins/ # 核心插件 (dragStore等) -│ │ └── stores/ # 状态管理 -│ └── views/ # 示例页面 +│ ├── src/ +│ │ ├── fauto/ # 设计器核心 +│ │ │ ├── components/ # UI组件 +│ │ │ ├── materials/ # 物料组件 +│ │ │ ├── designComponents/ # 设计组件库 +│ │ │ ├── plugins/ # 插件系统 +│ │ │ └── stores/ # 状态管理 +│ │ └── views/ # 示例页面 +│ └── vite.config.ts │ └── vue-template-service/ # 后端服务 └── src/ + ├── index.js # API入口 └── services/ - └── templateService.js # Vue模板解析修改 + └── templateService.js # 模板解析 ``` ---- +## 🔌 设计组件 -## 🎮 使用方法 +内置设计组件: -1. **选择页面** - 在左侧面板选择要编辑的 Vue 页面 -2. **开始拖拽** - 点击页面中的 el-row 或 el-col 元素 -3. **选择源层级** - 按 ↑↓ 键切换要移动的层级 -4. **移动到目标** - 拖动到目标元素上 -5. **选择目标层级** - 按 ↑↓ 键切换目标层级 -6. **确认位置** - 松开鼠标,选择放置方向(上/下/左/右/内部) -7. **自动保存** - 源文件自动更新,页面热刷新 +| 组件 | 描述 | +|------|------| +| 输入框 (TextInput) | 文本输入表单组件 | +| 单选器 (RadioSelect) | 单选按钮组组件 | +| 表格 (GridTable) | 数据表格组件 | ---- +### 添加自定义设计组件 -## 🤖 关于 AI 开发 +1. 在 `draggable-panels/src/fauto/designComponents/` 创建目录 +2. 添加配置文件 `index.json`: +```json +{ + "id": "MyComponent", + "name": "我的组件", + "icon": "🎯", + "description": "组件描述", + "defaultSpan": 12, + "metadata": { + "span": { + "label": "宽度", + "type": "number", + "min": 1, + "max": 24, + "target": "el-col", + "attr": ":span" + } + } +} +``` -本项目是一个 **AI 驱动开发**的实验性项目: +3. 添加模板文件 `template.html`: +```html + +
+ +
+
+``` -- **开发工具**:Qoder AI(AI 编程助手) -- **代码生成**:100% 由 AI 生成 -- **人工参与**:需求描述、测试反馈、方向指导 +## 📋 Vue页面规范 -这证明了 AI 辅助开发在复杂前端项目中的可行性,包括: -- 状态管理设计 -- 拖拽交互实现 -- AST 解析与修改 -- 多阶段工作流 +设计器要求Vue页面遵循以下结构: ---- +```vue + +``` -## 📄 相关文档 +## 🔗 API文档 -- [项目上下文.md](./项目上下文.md) - AI 协作快速恢复上下文 -- [项目设计文档.md](./项目设计文档.md) - 详细技术设计 +后端服务运行在 `http://localhost:3001` ---- +| 端点 | 方法 | 功能 | +|------|------|------| +| `/api/move-element` | POST | 移动元素 | +| `/api/insert-element` | POST | 插入新元素 | +| `/api/delete-element` | POST | 删除元素 | +| `/api/element-tree` | GET | 获取页面结构 | +| `/api/component-props` | GET | 获取组件属性 | +| `/api/update-props` | POST | 更新组件属性 | -## 📜 License +## 📚 文档 + +- [项目上下文](./项目上下文.md) - AI协作上下文 +- [项目设计文档](./项目设计文档.md) - 详细技术设计 +- [开发指南](./DEVELOPMENT.md) - 开发规范与扩展指南 + +## 🛠️ 技术栈 + +**前端** +- Vue 3.5 (Composition API) +- TypeScript +- Vite 7.3 +- Pinia 3.0 +- Element Plus + +**后端** +- Node.js +- Express +- @vue/compiler-sfc +- @vue/compiler-dom + +## 📄 License MIT License - ---- - -

- 🚀 Powered by Qoder AI -

diff --git a/draggable-panels/README.md b/draggable-panels/README.md new file mode 100644 index 0000000..c36a77a --- /dev/null +++ b/draggable-panels/README.md @@ -0,0 +1,21 @@ +# Draggable Panels - 前端项目 + +这是 Fauto Design 的前端项目,基于 Vue 3 + TypeScript + Vite 构建。 + +## 快速开始 + +```bash +npm install +npm run dev +``` + +访问:`http://localhost:5173/draggable` + +## 详细文档 + +请参阅项目根目录的文档: + +- [README.md](../README.md) - 项目介绍 +- [项目上下文.md](../项目上下文.md) - AI协作上下文 +- [项目设计文档.md](../项目设计文档.md) - 技术设计 +- [DEVELOPMENT.md](../DEVELOPMENT.md) - 开发指南 diff --git a/项目上下文.md b/项目上下文.md index 11e6b3c..d5dca58 100644 --- a/项目上下文.md +++ b/项目上下文.md @@ -1,6 +1,6 @@ # Vue页面可视化设计器 - 项目上下文 -> **用途**:AI 协作快速恢复上下文 +> **用途**:用于在新环境快速恢复 AI 协作上下文 > **更新时间**:2026-01-20 > **项目路径**:`d:/workspace/fauto-design` @@ -8,15 +8,17 @@ ## 📋 项目概述 -基于 **Vue3 + TypeScript + Vite + Element Plus** 的**可视化页面设计器**,通过拖拽操作直接编辑真实的Vue源文件。 +这是一个基于 **Vue3 + TypeScript + Vite + Element Plus** 的**可视化页面设计器**,通过拖拽操作直接编辑真实的Vue页面源文件。 ### 核心特性 -1. **直接解析Vue文件** - 动态扫描并渲染 `src/views` 下的页面 -2. **拖拽式设计** - 将组件拖拽到页面的 el-row/el-col 上 -3. **两阶段拖拽** - 源选择阶段 → 目标选择阶段 -4. **智能层级选择** - 键盘 ↑↓ 切换嵌套元素层级 -5. **源码实时修改** - 后端服务解析AST并修改Vue文件 +1. **直接解析Vue文件** - 动态扫描并渲染`src/views`下的页面 +2. **拖拽式设计** - 将设计组件拖拽到页面,支持四个方向放置 +3. **源码级修改** - 拖拽操作直接修改Vue源文件,支持热更新 +4. **智能层级选择** - 键盘方向键切换嵌套元素层级 +5. **实时视觉反馈** - 拖拽预览 + 拖放区域显示 +6. **元数据编辑** - 点击组件可编辑属性(宽度、尺寸、颜色等) +7. **多种删除方式** - 右键菜单、Del键、删除图标 --- @@ -25,152 +27,215 @@ ``` fauto-design/ ├── draggable-panels/ # 前端项目 -│ └── src/ -│ ├── fauto/ # 🔥 设计器核心 -│ │ ├── Designer.vue # 设计器主入口 -│ │ ├── components/ # 基础UI组件 -│ │ ├── materials/ # 物料组件系统 -│ │ │ ├── DesignCenter/ # 设计中心 -│ │ │ │ ├── InteractiveWrapper.vue # 交互注入器 -│ │ │ │ └── DropZone.vue # 拖放指示器 -│ │ │ ├── DesignComponentList/ # 设计组件列表 -│ │ │ └── PageManager/ # 页面管理 -│ │ ├── designComponents/ # 设计组件库 -│ │ ├── plugins/ # 🔌 核心插件 -│ │ │ ├── dragStore.ts # 拖拽状态管理 -│ │ │ ├── interactionStore.ts -│ │ │ └── pathUtils.ts # 路径工具 -│ │ └── stores/ # Pinia状态 -│ └── views/ # 示例页面 +│ ├── src/ +│ │ ├── fauto/ # 🔥 设计器核心代码 +│ │ │ ├── Designer.vue # 设计器主入口 +│ │ │ │ +│ │ │ ├── components/ # 基础UI组件 +│ │ │ │ ├── Header.vue # 顶部菜单栏 +│ │ │ │ ├── Footer.vue # 底部状态栏 +│ │ │ │ ├── MainLayout.vue # 三栏布局容器 +│ │ │ │ ├── Panel.vue # 面板容器 +│ │ │ │ └── Resizer.vue # 面板分隔器 +│ │ │ │ +│ │ │ ├── materials/ # 🎁 物料组件系统 +│ │ │ │ ├── PageManager/ # 页面管理(树形文件选择) +│ │ │ │ ├── DesignComponentList/ # 设计组件列表 +│ │ │ │ ├── DesignCenter/ # 设计中心(动态渲染页面) +│ │ │ │ │ ├── index.vue +│ │ │ │ │ ├── InteractiveWrapper.vue # 交互包装器 +│ │ │ │ │ ├── DropZone.vue # 拖放区域指示器 +│ │ │ │ │ └── DragPreview.vue # 拖拽预览 +│ │ │ │ ├── TreeViewer/ # 结构树(页面结构展示) +│ │ │ │ └── DataTable/ # 元数据编辑器 +│ │ │ │ +│ │ │ ├── designComponents/ # 🎨 设计组件库 +│ │ │ │ ├── TextInput/ # 输入框 +│ │ │ │ │ ├── index.json # 组件配置+元数据schema +│ │ │ │ │ └── template.html # 组件模板 +│ │ │ │ ├── RadioSelect/ # 单选器 +│ │ │ │ └── GridTable/ # 表格 +│ │ │ │ +│ │ │ ├── plugins/ # 🔌 插件系统 +│ │ │ │ ├── index.ts # 统一导出 +│ │ │ │ ├── interactionStore.ts # 交互事件钩子 +│ │ │ │ ├── dragStore.ts # 拖拽状态管理 +│ │ │ │ └── pathUtils.ts # 结构化路径工具 +│ │ │ │ +│ │ │ ├── stores/ # 🗄️ Pinia状态管理 +│ │ │ │ ├── panelStore.ts # 面板布局状态 +│ │ │ │ ├── designStore.ts # 设计组件元数据 +│ │ │ │ └── vueFileStore.ts# Vue文件选择状态 +│ │ │ │ +│ │ │ └── types/ # 📝 类型定义 +│ │ │ +│ │ └── views/ # 📄 示例页面 +│ │ +│ └── vite.config.ts │ -└── vue-template-service/ # 后端服务(Node.js) +└── vue-template-service/ # 🖥️ 后端服务 └── src/ - ├── index.js # Express API + ├── index.js # Express服务入口 └── services/ - └── templateService.js # Vue模板解析修改 + └── templateService.js # Vue模板解析与修改服务 ``` --- ## 🔑 核心技术要点 -### 1. Vue页面规范 ⭐ +### 1. **两阶段拖拽机制** ⭐ -**强制规则**:template 第一层级**有且仅有一个 el-row** - -```vue - -``` - -### 2. 结构化路径ID ⭐ - -**格式**:`r{n}c{m}...`(r=row, c=col, 数字=索引从1开始) - -**示例**:`r1c2r1c3` = 第1个row → 第2个col → 第1个row → 第3个col - -### 3. 两阶段拖拽 ⭐⭐ - -| 阶段 | 触发 | ↑↓键功能 | 拖放方向 | -|------|------|----------|---------| -| **源选择** | 点击元素 | 切换源层级 | 不显示 | -| **目标选择** | 移到其他元素 | 切换目标层级 | 显示 | +**第一阶段(源选择)**:鼠标按下开始,收集当前位置的层级节点 +**第二阶段(目标选择)**:移动到新位置,选择目标和方向 ```typescript -// dragStore.ts 核心状态 -dragPhase: 'source' | 'target' -confirmedSource: { path, type, element } +// dragStore.ts +startDragFromCanvas(path, type, element) → 进入源选择阶段 +enterTargetPhase(element) → 进入目标选择阶段 +confirmDrop(pagePath) → 调用后端API执行移动 ``` -### 4. 跨类型拖放 ⭐ +### 2. **结构化路径ID** ⭐ -- **同类型**:el-row→el-row 显示上/下,el-col→el-col 显示左/右 -- **跨类型**:el-row→el-col 或 el-col→el-row 显示"放入内部"(inside) +**格式**:`r{n}c{m}r{x}c{y}...` +- `r`: el-row +- `c`: el-col +- 数字:同级索引(从1开始) -### 5. 后端模板服务 ⭐⭐ +**示例**:`r1c2r1c3` 表示: +- 第1个el-row → 第2个el-col → 第1个el-row → 第3个el-col -**API**:`POST http://localhost:3001/api/move-element` +### 3. **设计组件标识** ⭐ + +设计组件使用 `data-component` 属性标识类型: + +```html + +
+ + + +
+
+``` + +### 4. **元数据Schema定义** ⭐ + +设计组件的 index.json 定义可编辑属性: ```json { - "pagePath": "views/TestPage1.vue", - "source": "r1c1", - "target": "r1c2", - "direction": "right" // top|bottom|left|right|inside + "id": "TextInput", + "name": "输入框", + "metadata": { + "span": { "label": "宽度", "type": "number", "min": 1, "max": 24, "target": "el-col", "attr": ":span" }, + "label": { "label": "标签", "type": "text", "target": "el-form-item", "attr": "label" }, + "placeholder": { "label": "占位符", "type": "text", "target": "el-input", "attr": "placeholder" } + } } ``` -**技术栈**: -- `@vue/compiler-sfc` - 解析Vue SFC -- `@vue/compiler-dom` - 解析template AST -- 字符串操作移动元素(保持原始格式) +--- + +## 🔗 后端API + +| 端点 | 方法 | 功能 | +|------|------|------| +| `/api/move-element` | POST | 移动元素(画布内拖拽) | +| `/api/insert-element` | POST | 插入新元素(设计组件拖入) | +| `/api/delete-element` | POST | 删除元素 | +| `/api/element-tree` | GET | 获取页面结构树 | +| `/api/component-props` | GET | 获取组件属性值 | +| `/api/update-props` | POST | 更新组件属性值 | --- -## 🎯 数据流 +## 🎯 数据流图 ``` -用户点击元素开始拖拽 -↓ -dragStore.startDragFromCanvas() → 源选择阶段 -↓ -按↑↓键切换源层级 -↓ -移动到其他元素 → dragStore.enterTargetPhase() → 目标选择阶段 -↓ -按↑↓键切换目标层级,显示DropZone -↓ -松开鼠标 → dragStore.confirmDrop() -↓ -调用后端API → templateService.moveElement() -↓ -修改Vue源文件 → 页面热更新 +┌─────────────────────────────────────────────────────────────────┐ +│ 用户操作 │ +└───────────────────────────┬─────────────────────────────────────┘ + │ + ┌───────────────────┼───────────────────┐ + ▼ ▼ ▼ +┌───────────────┐ ┌───────────────┐ ┌───────────────┐ +│ 页面管理器 │ │ 设计组件列表 │ │ 设计中心 │ +│ (选择Vue文件) │ │ (拖拽组件) │ │ (编辑区域) │ +└───────┬───────┘ └───────┬───────┘ └───────┬───────┘ + │ │ │ + ▼ ▼ ▼ +┌───────────────────────────────────────────────────────────────┐ +│ Pinia 状态管理 │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ vueFileStore │ │ dragStore │ │ designStore │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +└───────────────────────────┬───────────────────────────────────┘ + │ + ▼ +┌───────────────────────────────────────────────────────────────┐ +│ vue-template-service │ +│ (Node.js后端服务) │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ templateService.js - Vue模板AST解析与修改 │ │ +│ └─────────────────────────────────────────────────────────┘ │ +└───────────────────────────┬───────────────────────────────────┘ + │ + ▼ +┌───────────────────────────────────────────────────────────────┐ +│ Vue源文件 │ +│ (src/views/*.vue) │ +└───────────────────────────────────────────────────────────────┘ ``` --- ## 🛠️ 快速开始 +### 启动前端 ```bash -# 启动前端(端口5173) cd draggable-panels +npm install npm run dev - -# 启动后端(端口3001) -cd vue-template-service -node src/index.js +# 访问 http://localhost:5173/draggable ``` -**访问**:http://localhost:5173/draggable +### 启动后端 +```bash +cd vue-template-service +npm install +node src/index.js +# 服务运行在 http://localhost:3001 +``` --- -## 📝 关键文件 +## 📝 Vue页面规范 -| 文件 | 用途 | -|------|------| -| `plugins/dragStore.ts` | 拖拽状态、两阶段逻辑、层级选择 | -| `materials/DesignCenter/InteractiveWrapper.vue` | 交互注入、键盘监听、拖放确认 | -| `materials/DesignCenter/DropZone.vue` | 拖放方向指示器 | -| `vue-template-service/src/services/templateService.js` | AST解析、元素移动 | +**强制规则**:template的第一层级**有且仅有一个el-row** + +```vue + +``` --- -## 💡 注意事项 +## 📚 相关文档 -1. 拖拽时会禁用文本选择(`user-select: none`) -2. 使用全局鼠标事件获取悬停位置(避免 pointer-events 问题) -3. 移动元素时保持相对缩进关系 -4. 前后端都需要运行才能完成拖放后的源码修改 +- **详细设计文档**:`项目设计文档.md` +- **开发者指南**:`DEVELOPMENT.md` +- **README**:`README.md` --- -**最后更新**:2026-01-20 +**最后更新**:2026-01-20 +**AI协作建议**:优先阅读"核心技术要点"和"后端API"部分,理解系统的数据流和交互方式 diff --git a/项目设计文档.md b/项目设计文档.md index 0c6f3cb..e17de41 100644 --- a/项目设计文档.md +++ b/项目设计文档.md @@ -1,366 +1,423 @@ # Vue页面可视化设计器 - 项目设计文档 -## 项目概述 +## 1. 项目概述 -本项目是一个基于 **Vite + Vue3 + TypeScript + Element Plus** 的可视化页面设计器,通过拖拽操作直接编辑真实的Vue源文件,实现低代码页面快速构建。 +本项目是一个基于 **Vue3 + TypeScript + Element Plus** 的可视化页面设计器,通过拖拽操作直接编辑真实的Vue源文件,实现低代码页面快速构建。 -## 技术架构 +### 1.1 核心特性 -### 技术栈 +| 特性 | 描述 | +|------|------| +| 直接解析Vue文件 | 动态渲染真实的.vue页面 | +| 源码级修改 | 拖拽操作直接修改Vue源文件 | +| 智能层级选择 | 键盘方向键切换嵌套元素层级 | +| 元数据编辑 | 可视化编辑组件属性 | +| 多视图联动 | 设计中心、结构树、元数据面板同步选中 | +| 热更新 | 修改后自动刷新预览 | -| 类别 | 技术 | 版本 | -|------|------|------| -| 构建工具 | Vite | 7.3.0 | -| 前端框架 | Vue 3 (Composition API) | 3.5.24 | -| UI组件库 | Element Plus | - | -| 状态管理 | Pinia | 3.0.4 | -| 类型系统 | TypeScript | - | -| 后端运行时 | Node.js | 22.x | -| 后端框架 | Express | - | -| 模板解析 | @vue/compiler-sfc, @vue/compiler-dom | - | - -### 系统架构 +### 1.2 技术架构 ``` -┌─────────────────────────────────────────────────────────────┐ -│ 前端 (Vue3 + Vite) │ -├─────────────────────────────────────────────────────────────┤ -│ Designer.vue │ -│ ├── Header (顶部菜单) │ -│ ├── MainLayout (三栏布局) │ -│ │ ├── 左侧面板 (PageManager, DesignComponentList) │ -│ │ ├── 中间面板 (DesignCenter + InteractiveWrapper) │ -│ │ └── 右侧面板 (属性编辑器 - 待实现) │ -│ └── Footer (状态栏 - 显示拖放记录) │ -├─────────────────────────────────────────────────────────────┤ -│ 状态管理层 │ -│ ├── dragStore.ts (拖拽状态、两阶段逻辑、层级选择) │ -│ ├── panelStore.ts (面板布局状态) │ -│ ├── designStore.ts (设计组件元数据) │ -│ └── vueFileStore.ts (Vue文件选择状态) │ -└─────────────────────────────────────────────────────────────┘ - │ +┌─────────────────────────────────────────────────────────────────┐ +│ 前端应用 (Vue3 + Vite) │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │ 页面管理 │ │设计组件列│ │ 设计中心 │ │ 元数据 │ │ +│ │ │ │表 │ │ │ │ 编辑器 │ │ +│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ +│ │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ Pinia 状态管理 + 插件系统 │ │ +│ │ dragStore │ interactionStore │ designStore │ vueFileStore│ │ +│ └──────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ │ HTTP API ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 后端 (Node.js + Express) │ -├─────────────────────────────────────────────────────────────┤ -│ API: POST /api/move-element │ -│ ├── 接收: { pagePath, source, target, direction } │ -│ └── 处理: templateService.moveElement() │ -├─────────────────────────────────────────────────────────────┤ -│ templateService.js │ -│ ├── parseSFC() - 解析Vue单文件组件 │ -│ ├── parseTemplate() - 解析template获取AST │ -│ ├── findElementByPath() - 根据路径定位元素 │ -│ ├── moveElement() - 移动元素(同级前后) │ -│ └── moveElementInside() - 移动元素(放入内部) │ -└─────────────────────────────────────────────────────────────┘ - │ +┌─────────────────────────────────────────────────────────────────┐ +│ 后端服务 (Node.js + Express) │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ templateService - Vue模板AST解析与修改 │ │ +│ │ @vue/compiler-sfc + @vue/compiler-dom │ │ +│ └──────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ + │ 文件操作 ▼ -┌─────────────────────────────────────────────────────────────┐ -│ Vue源文件 (src/views/) │ -└─────────────────────────────────────────────────────────────┘ +┌─────────────────────────────────────────────────────────────────┐ +│ Vue源文件 (src/views/*.vue) │ +└─────────────────────────────────────────────────────────────────┘ ``` --- -## 核心功能模块 +## 2. 核心模块设计 -### 1. 拖拽状态管理 (dragStore.ts) +### 2.1 前端模块 -#### 核心状态 +#### 2.1.1 设计中心 (DesignCenter) +**职责**:动态渲染选中的Vue页面,注入交互事件 + +**核心组件**: +- `index.vue` - 主容器,动态加载Vue页面组件 +- `InteractiveWrapper.vue` - 交互包装器,注入事件监听 +- `DropZone.vue` - 拖放区域指示器 +- `DragPreview.vue` - 拖拽跟随预览 + +**交互事件注入流程**: ```typescript -// 拖拽阶段 -dragPhase: 'source' | 'target' +// 1. 动态渲染页面组件 + -// 拖拽源信息 -dragSource: { - type: 'design-component' | 'canvas-element' - componentId?: string - componentName?: string - path?: string - elementType?: 'er' | 'ec' -} - -// 已确定的源元素(进入目标阶段时保存) -confirmedSource: { - path: string - type: 'er' | 'ec' - element: HTMLElement -} - -// 层级节点列表(从深到浅排序) -hierarchyNodes: HierarchyNode[] - -// 当前选中的层级索引 -selectedHierarchyIndex: number - -// 拖放方向 -hoverDirection: 'top' | 'bottom' | 'left' | 'right' | 'inside' | null -``` - -#### 两阶段拖拽流程 - -``` -源选择阶段 (source) -├── 触发: startDragFromCanvas(path, type, element) -├── 功能: -│ ├── 构建当前元素的层级列表 -│ └── ↑↓键切换要拖拽的源层级 -├── 特点: -│ ├── 不显示DropZone -│ └── 松开鼠标取消拖拽 -└── 退出: 移动到其他元素 → enterTargetPhase() - -目标选择阶段 (target) -├── 触发: enterTargetPhase(targetElement) -├── 功能: -│ ├── 保存确定的源元素到 confirmedSource -│ ├── 构建目标元素的层级列表 -│ └── ↑↓键切换目标层级 -├── 特点: -│ ├── 显示DropZone -│ └── 根据源/目标类型显示方向 -└── 退出: 松开鼠标 → confirmDrop() -``` - -#### 跨类型拖放逻辑 - -```typescript -const isCrossTypeDrop = computed(() => { - if (dragPhase.value !== 'target') return false - if (!confirmedSource.value || !selectedNode.value) return false - - // 源类型与目标类型不同 - return confirmedSource.value.type !== selectedNode.value.type -}) - -// 跨类型时显示 'inside' 方向 -if (isCrossTypeDrop.value) { - hoverDirection.value = 'inside' -} -``` - -### 2. 交互注入器 (InteractiveWrapper.vue) - -#### 核心职责 - -1. **动态渲染Vue页面** - 使用 `defineAsyncComponent` 加载选中的页面 -2. **注入交互事件** - 为所有 el-row/el-col 绑定事件 -3. **监听DOM变化** - 使用 MutationObserver 检测新元素 -4. **键盘事件处理** - ↑↓切换层级,Esc取消 - -#### 事件绑定 - -```typescript -const bindElementEvents = (element, type, path) => { - // 标记已绑定 - element.setAttribute('data-fauto-bindend', 'true') - element.setAttribute('data-path', path) - element.classList.add('fauto-interactive') - - // 悬停事件 - 进入目标阶段 - element.addEventListener('mouseenter', (e) => { - if (dragStore.isDragging) { - const sourcePath = dragStore.confirmedSource?.path || dragStore.dragSource?.path - if (sourcePath && path !== sourcePath && !path.startsWith(sourcePath)) { - dragStore.enterTargetPhase(element) - } - } - }) - - // 按下事件 - 开始拖拽 - element.addEventListener('mousedown', (e) => { - if (!dragStore.isDragging) { - dragStore.startDragFromCanvas(path, type, element) - } +// 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 -// 全局鼠标移动 - 更新拖放方向 -const handleGlobalMouseMove = (e) => { - if (dragStore.isDragging && dragStore.dragPhase === 'target') { - dragStore.updateDirectionFromMouse(e.clientX, e.clientY) - } -} - -// 全局鼠标松开 - 确认拖放 -const handleGlobalMouseUp = async () => { - if (dragStore.isDragging && dragStore.dragPhase === 'target') { - await dragStore.confirmDrop(vueFileStore.selectedFilePath) - } - dragStore.endDrag() -} -``` - -### 3. 后端模板服务 (templateService.js) - -#### 路径解析 - -```javascript -// 解析路径: "r1c2r1" → [{type:'r',index:1}, {type:'c',index:2}, {type:'r',index:1}] -function parsePath(pathStr) { - const nodes = [] - const regex = /([rc])(\d+)/g - let match - while ((match = regex.exec(pathStr)) !== null) { - nodes.push({ type: match[1], index: parseInt(match[2], 10) }) - } - return nodes -} -``` - -#### 元素定位 - -```javascript -function findElementByPath(ast, pathStr) { - const pathNodes = parsePath(pathStr) - let currentChildren = ast.children - let currentNode = null +interface DragStore { + // 状态 + isDragging: boolean + dragPhase: 'source' | 'target' // 两阶段拖拽 + dragSource: DragSource | null + hierarchyNodes: HierarchyNode[] // 层级节点列表 + selectedHierarchyIndex: number // 当前选中层级 + hoverDirection: Direction | null - for (const pathNode of pathNodes) { - const targetType = pathNode.type === 'r' ? 'el-row' : 'el-col' - let count = 0 - - for (const child of currentChildren) { - if (child.type === 1 && child.tag === targetType) { - count++ - if (count === pathNode.index) { - currentNode = child - currentChildren = child.children || [] - break + // 方法 + 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` +- 内层 `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": "...", + "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": [] } - } + ] } - } - return currentNode -} -``` - -#### 缩进调整算法 - -```javascript -function adjustIndentation(sourceText, targetIndent) { - const lines = sourceText.split('\n') - - // 获取第一行的原始缩进 - const firstLineIndent = getIndentLevel(lines[0]) - - // 计算缩进差值 - const indentDiff = targetIndent.length - firstLineIndent - - // 对每一行应用缩进差值(保持相对缩进) - return lines.map(line => { - if (!line.trim()) return '' - const currentIndent = getIndentLevel(line) - const newIndent = Math.max(0, currentIndent + indentDiff) - return ' '.repeat(newIndent) + line.trimStart() - }).join('\n') -} -``` - -#### 元素移动(从后向前处理) - -```javascript -// 关键:从后向前处理,避免偏移量错误 -if (deleteStart > insertPosition) { - // 删除位置在后:先删除,再插入 - result = content.substring(0, deleteStart) + content.substring(deleteEnd) - result = result.substring(0, insertPosition) + insertText + result.substring(insertPosition) -} else { - // 删除位置在前:先删除,调整插入位置 - const deletedLength = deleteEnd - deleteStart - const adjustedInsertPos = insertPosition - deletedLength - result = content.substring(0, deleteStart) + content.substring(deleteEnd) - result = result.substring(0, adjustedInsertPos) + insertText + result.substring(adjustedInsertPos) + ] } ``` --- -## 开发规范 +## 5. 交互设计 -### Vue页面结构规范 +### 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 ``` -### 代码规范 +### 7.2 代码规范 -1. 使用 TypeScript 严格模式 -2. 组件使用 Composition API -3. Props 必须明确定义类型 -4. 样式使用 scoped 避免污染 - -### 插件开发规范 - -所有与页面交互相关的代码放在 `src/fauto/plugins/` 目录: -- 状态管理类使用 Pinia defineStore -- 工具函数单独文件导出 -- 统一通过 `index.ts` 导出 +- TypeScript 严格模式 +- Composition API 组织代码 +- Props 必须定义类型 +- 样式使用 scoped +- 插件代码放 `fauto/plugins/` --- -## API 接口 +## 8. 版本信息 -### 移动元素 - -``` -POST /api/move-element - -Request: -{ - "pagePath": "views/TestPage1.vue", // 相对于 src 的路径 - "source": "r1c1", // 源元素路径 - "target": "r1c2", // 目标元素路径 - "direction": "right" // top|bottom|left|right|inside -} - -Response: -{ - "success": true, - "message": "元素移动成功" -} -``` +- **前端框架**: Vue 3.5.24 +- **构建工具**: Vite 7.3.0 +- **状态管理**: Pinia 3.0.4 +- **UI框架**: Element Plus +- **后端运行时**: Node.js +- **模板解析**: @vue/compiler-sfc, @vue/compiler-dom --- -## 待实现功能 - -1. **属性编辑器** - 展示和编辑选中组件的属性 -2. **撤销/重做** - 操作历史记录 -3. **设计组件拖入** - 从组件列表拖入新组件 -4. **预览模式** - 隐藏交互层预览最终效果 -5. **导出功能** - 导出设计结果 - ---- - -## 已知问题 - -1. 拖放操作有概率生成错误HTML结构(已基本修复) -2. 复杂嵌套结构下缩进可能不完美 - ---- - -**最后更新**:2026-01-20 +**文档更新时间**:2026-01-20