ai牛逼
This commit is contained in:
597
DEVELOPMENT.md
Normal file
597
DEVELOPMENT.md
Normal file
@@ -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
|
||||
<script setup lang="ts">
|
||||
// 1. 导入语句
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import type { PropType } from 'vue'
|
||||
import { useStore } from '../stores/xxx'
|
||||
|
||||
// 2. Props定义(必须有类型)
|
||||
const props = defineProps<{
|
||||
title: string
|
||||
count?: number
|
||||
}>()
|
||||
|
||||
// 3. Emits定义
|
||||
const emit = defineEmits<{
|
||||
(e: 'update', value: string): void
|
||||
(e: 'close'): void
|
||||
}>()
|
||||
|
||||
// 4. 响应式状态
|
||||
const loading = ref(false)
|
||||
const data = ref<DataItem[]>([])
|
||||
|
||||
// 5. 计算属性
|
||||
const isEmpty = computed(() => data.value.length === 0)
|
||||
|
||||
// 6. 方法
|
||||
const fetchData = async () => { ... }
|
||||
|
||||
// 7. 生命周期
|
||||
onMounted(() => { ... })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 模板内容 -->
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 使用scoped避免样式污染 */
|
||||
</style>
|
||||
```
|
||||
|
||||
### 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
|
||||
<template>
|
||||
<!-- ✅ 正确:单一el-row根元素 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">...</el-col>
|
||||
<el-col :span="12">...</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
```
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<!-- ❌ 错误:多个根元素 -->
|
||||
<el-row>...</el-row>
|
||||
<el-row>...</el-row>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 3.2 设计组件标识
|
||||
|
||||
页面中的设计组件需要 `data-component` 属性:
|
||||
|
||||
```html
|
||||
<el-col :span="12">
|
||||
<div class="design-component" data-component="输入框">
|
||||
<el-form-item label="用户名">
|
||||
<el-input v-model="username" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-col>
|
||||
```
|
||||
|
||||
**关键点**:
|
||||
- `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 :span="12">
|
||||
<div class="design-component design-my-input" data-component="我的输入框">
|
||||
<el-form-item label="标签">
|
||||
<el-input v-model="value" placeholder="请输入"></el-input>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-col>
|
||||
```
|
||||
|
||||
**模板规范**:
|
||||
- 外层必须是 `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
|
||||
<!-- DataTable/index.vue -->
|
||||
<template v-else-if="field.type === 'myNewType'">
|
||||
<MyNewTypeEditor :value="localValues[key]" @change="handleChange" />
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 物料组件开发
|
||||
|
||||
### 5.1 创建步骤
|
||||
|
||||
1. 在 `draggable-panels/src/fauto/materials/` 创建目录
|
||||
2. 添加 `index.vue` 组件文件
|
||||
3. 添加 `index.json` 配置文件
|
||||
4. 在 `materials/index.ts` 注册
|
||||
|
||||
### 5.2 组件文件 (index.vue)
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import config from './index.json'
|
||||
|
||||
// 组件逻辑
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="my-material">
|
||||
<div class="material-header">
|
||||
<span class="title">{{ config.name }}</span>
|
||||
</div>
|
||||
<div class="material-body">
|
||||
<!-- 物料组件内容 -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.my-material {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### 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<MyState>(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 <pid>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**文档更新时间**:2026-01-20
|
||||
254
README.md
254
README.md
@@ -1,166 +1,192 @@
|
||||
# Fauto Design - Vue页面可视化设计器
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/Vue-3.5-4FC08D?logo=vue.js" alt="Vue 3">
|
||||
<img src="https://img.shields.io/badge/TypeScript-5.x-3178C6?logo=typescript" alt="TypeScript">
|
||||
<img src="https://img.shields.io/badge/Vite-7.3-646CFF?logo=vite" alt="Vite">
|
||||
<img src="https://img.shields.io/badge/Element%20Plus-Latest-409EFF" alt="Element Plus">
|
||||
<img src="https://img.shields.io/badge/Node.js-22.x-339933?logo=node.js" alt="Node.js">
|
||||
<img src="https://img.shields.io/badge/Vue-3.5-4FC08D?style=flat-square&logo=vue.js" alt="Vue 3.5">
|
||||
<img src="https://img.shields.io/badge/TypeScript-5.0-3178C6?style=flat-square&logo=typescript" alt="TypeScript">
|
||||
<img src="https://img.shields.io/badge/Vite-7.3-646CFF?style=flat-square&logo=vite" alt="Vite">
|
||||
<img src="https://img.shields.io/badge/Element%20Plus-Latest-409EFF?style=flat-square" alt="Element Plus">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<strong>🤖 本项目完全由 Qoder AI 开发,所有代码均为 AI 生成</strong>
|
||||
</p>
|
||||
## 📖 简介
|
||||
|
||||
---
|
||||
**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 <repository-url>
|
||||
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
|
||||
<el-col :span="12">
|
||||
<div class="design-component" data-component="我的组件">
|
||||
<!-- 组件内容 -->
|
||||
</div>
|
||||
</el-col>
|
||||
```
|
||||
|
||||
- **开发工具**:Qoder AI(AI 编程助手)
|
||||
- **代码生成**:100% 由 AI 生成
|
||||
- **人工参与**:需求描述、测试反馈、方向指导
|
||||
## 📋 Vue页面规范
|
||||
|
||||
这证明了 AI 辅助开发在复杂前端项目中的可行性,包括:
|
||||
- 状态管理设计
|
||||
- 拖拽交互实现
|
||||
- AST 解析与修改
|
||||
- 多阶段工作流
|
||||
设计器要求Vue页面遵循以下结构:
|
||||
|
||||
---
|
||||
```vue
|
||||
<template>
|
||||
<!-- 第一层级必须且只能有一个el-row -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<!-- 设计组件需要data-component属性 -->
|
||||
<div class="design-component" data-component="组件名">
|
||||
<!-- 内容 -->
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 📄 相关文档
|
||||
## 🔗 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
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<strong>🚀 Powered by Qoder AI</strong>
|
||||
</p>
|
||||
|
||||
21
draggable-panels/README.md
Normal file
21
draggable-panels/README.md
Normal file
@@ -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) - 开发指南
|
||||
273
项目上下文.md
273
项目上下文.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文件
|
||||
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
|
||||
<template>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">内容</el-col>
|
||||
<el-col :span="12">
|
||||
<el-row> <!-- 支持嵌套 -->
|
||||
<el-col :span="24">嵌套内容</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 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
|
||||
<el-col :span="12">
|
||||
<div class="design-component" data-component="输入框">
|
||||
<el-form-item label="文本输入">
|
||||
<el-input v-model="inputValue" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-col>
|
||||
```
|
||||
|
||||
### 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
|
||||
<template>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="design-component" data-component="输入框">
|
||||
<!-- 内容 -->
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 注意事项
|
||||
## 📚 相关文档
|
||||
|
||||
1. 拖拽时会禁用文本选择(`user-select: none`)
|
||||
2. 使用全局鼠标事件获取悬停位置(避免 pointer-events 问题)
|
||||
3. 移动元素时保持相对缩进关系
|
||||
4. 前后端都需要运行才能完成拖放后的源码修改
|
||||
- **详细设计文档**:`项目设计文档.md`
|
||||
- **开发者指南**:`DEVELOPMENT.md`
|
||||
- **README**:`README.md`
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2026-01-20
|
||||
**AI协作建议**:优先阅读"核心技术要点"和"后端API"部分,理解系统的数据流和交互方式
|
||||
|
||||
641
项目设计文档.md
641
项目设计文档.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. 动态渲染页面组件
|
||||
<component :is="selectedPageComponent" />
|
||||
|
||||
// 拖拽源信息
|
||||
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)
|
||||
}
|
||||
}
|
||||
interface DragStore {
|
||||
// 状态
|
||||
isDragging: boolean
|
||||
dragPhase: 'source' | 'target' // 两阶段拖拽
|
||||
dragSource: DragSource | null
|
||||
hierarchyNodes: HierarchyNode[] // 层级节点列表
|
||||
selectedHierarchyIndex: number // 当前选中层级
|
||||
hoverDirection: Direction | null
|
||||
|
||||
// 全局鼠标松开 - 确认拖放
|
||||
const handleGlobalMouseUp = async () => {
|
||||
if (dragStore.isDragging && dragStore.dragPhase === 'target') {
|
||||
await dragStore.confirmDrop(vueFileStore.selectedFilePath)
|
||||
}
|
||||
dragStore.endDrag()
|
||||
// 方法
|
||||
startDragFromCanvas(path, type, element) // 开始画布内拖拽
|
||||
startDragFromComponentList(id, name, template) // 开始组件列表拖拽
|
||||
enterTargetPhase(element) // 进入目标选择阶段
|
||||
selectParentLevel() // 选择父级(↑键)
|
||||
selectChildLevel() // 选择子级(↓键)
|
||||
confirmDrop(pagePath) // 确认拖放
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 后端模板服务 (templateService.js)
|
||||
**interactionStore** - 交互事件钩子
|
||||
```typescript
|
||||
interface InteractionStore {
|
||||
hoverTarget: InteractionTarget | null
|
||||
selectedTarget: InteractionTarget | null
|
||||
|
||||
#### 路径解析
|
||||
|
||||
```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
|
||||
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
|
||||
function findElementByPath(ast, pathStr) {
|
||||
const pathNodes = parsePath(pathStr)
|
||||
let currentChildren = ast.children
|
||||
let currentNode = null
|
||||
// 使用 @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'
|
||||
|
||||
for (const pathNode of pathNodes) {
|
||||
const targetType = pathNode.type === 'r' ? 'el-row' : 'el-col'
|
||||
let count = 0
|
||||
// 通过AST定位元素,使用字符串操作修改(保持原始格式)
|
||||
```
|
||||
|
||||
for (const child of currentChildren) {
|
||||
if (child.type === 1 && child.tag === targetType) {
|
||||
count++
|
||||
if (count === pathNode.index) {
|
||||
currentNode = child
|
||||
currentChildren = child.children || []
|
||||
break
|
||||
---
|
||||
|
||||
## 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return currentNode
|
||||
}
|
||||
```
|
||||
|
||||
#### 缩进调整算法
|
||||
### 3.3 设计组件模板
|
||||
|
||||
```javascript
|
||||
function adjustIndentation(sourceText, targetIndent) {
|
||||
const lines = sourceText.split('\n')
|
||||
```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>
|
||||
```
|
||||
|
||||
// 获取第一行的原始缩进
|
||||
const firstLineIndent = getIndentLevel(lines[0])
|
||||
**关键点**:
|
||||
- 外层必须是 `el-col`
|
||||
- 内层 `div` 必须有 `data-component` 属性标识组件类型
|
||||
- 使用 `class="design-component"` 标识设计组件
|
||||
|
||||
// 计算缩进差值
|
||||
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')
|
||||
## 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"
|
||||
}
|
||||
```
|
||||
|
||||
```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)
|
||||
#### 删除元素
|
||||
```
|
||||
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. 交互设计
|
||||
|
||||
### 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
|
||||
<template>
|
||||
<!-- 第一层级必须且只能有一个el-row -->
|
||||
<el-row class="page-container" :gutter="20">
|
||||
<!-- el-row 内只能包含 el-col -->
|
||||
<el-col :span="12">
|
||||
<!-- el-col 内可以放设计组件或嵌套 el-row -->
|
||||
<div class="design-component">内容</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="24">嵌套内容</el-col>
|
||||
</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 代码规范
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user