项目架构
本文档介绍 UEditor Plus Designer 的整体架构设计。
架构概览
┌─────────────────────────────────────────┐
│ User Interface │
│ (Vue Components / React Wrapper) │
└─────────────────┬───────────────────────┘
│
┌─────────────────▼───────────────────────┐
│ Composables Layer │
│ (useMaterial, useSection, etc.) │
└─────────────────┬───────────────────────┘
│
┌─────────────────▼───────────────────────┐
│ Core Layer │
│ (DesignerCore class) │
└─────────────────┬───────────────────────┘
│
┌─────────────────▼───────────────────────┐
│ UEditor Integration │
│ (UEditor Plus Editor) │
└─────────────────────────────────────────┘分层设计
1. 核心层 (Core Layer)
文件: src/core/DesignerCore.ts
框架无关的业务逻辑层,封装编辑器核心功能。
职责:
- UEditor 生命周期管理
- Section 管理和操作
- 事件系统
- 编辑器配置
关键类:
typescript
class DesignerCore {
private editor: any
private eventBus: EventEmitter
async init(): Promise<void>
getEditor(): any
insertSection(html: string): void
on(event: string, callback: Function): void
off(event: string, callback?: Function): void
destroy(): void
}2. 组合式函数层 (Composables Layer)
目录: src/composables/
Vue 3 特定的状态管理和逻辑复用。
模块:
useMaterial
文件: src/composables/useMaterial.ts
素材加载和管理。
typescript
export function useMaterial(config: DesignerConfig) {
// 数据
const materials = ref<MaterialItem[]>([])
const categories = ref<MaterialCategory[]>([])
const loading = ref(false)
// 方法
const loadCategories = async () => {}
const loadMaterials = async (params) => {}
return {
materials,
categories,
loading,
loadCategories,
loadMaterials
}
}useSection
文件: src/composables/useSection.ts
Section 状态管理。
typescript
export function useSection(designerCore: DesignerCore) {
// 数据
const currentSection = ref<HTMLElement | null>(null)
const sectionData = ref<SectionData | null>(null)
// 方法
const selectSection = (element: HTMLElement) => {}
const updateSection = (data: Partial<SectionData>) => {}
return {
currentSection,
sectionData,
selectSection,
updateSection
}
}useSectionActions
文件: src/composables/useSectionActions.ts
Section 操作逻辑。
typescript
export function useSectionActions(
currentSection: Ref<HTMLElement | null>,
designerCore: DesignerCore
) {
const moveUp = () => {}
const moveDown = () => {}
const duplicate = () => {}
const remove = () => {}
return {
moveUp,
moveDown,
duplicate,
remove
}
}3. 组件层 (Components Layer)
目录: src/components/
Vue UI 组件实现。
Designer.vue
主编辑器组件,整合所有功能。
vue
<template>
<div class="designer-container">
<MaterialPanel />
<div class="editor-wrapper">
<div ref="editorContainer"></div>
<SectionTools v-if="currentSection" />
</div>
</div>
</template>MaterialPanel.vue
素材面板,显示分类和素材列表。
vue
<template>
<div class="material-panel">
<div class="categories">...</div>
<div class="materials">...</div>
</div>
</template>SectionTools.vue
Section 工具栏,提供编辑操作。
vue
<template>
<div class="section-tools">
<button @click="moveUp">上移</button>
<button @click="moveDown">下移</button>
<!-- ... -->
</div>
</template>4. 工具层 (Utils Layer)
目录: src/utils/
纯函数和工具模块。
config.ts
配置常量和默认值。
typescript
export const Config = {
DefaultCategoryLoader: () => Promise.resolve([]),
DefaultStyleLoader: () => Promise.resolve({ records: [], total: 0 }),
SectionClass: 'pb-section',
SectionActiveClass: 'pb-section-active'
}ueditor-loader.ts
UEditor 动态加载。
typescript
export async function loadUEditor(path: string): Promise<void> {
// 加载 UEditor 脚本
}style-processor.ts
样式处理工具。
typescript
export class StyleProcessor {
static setWidth(element: HTMLElement, width: number): void {}
static setRotate(element: HTMLElement, rotate: number): void {}
static setOpacity(element: HTMLElement, opacity: number): void {}
}数据流
素材加载流程
用户打开素材面板
↓
MaterialPanel 组件
↓
useMaterial.loadMaterials()
↓
调用 config.styleLoader
↓
API 请求
↓
更新 materials ref
↓
组件自动更新Section 操作流程
用户点击 Section
↓
Designer 组件捕获事件
↓
useSection.selectSection()
↓
更新 currentSection ref
↓
SectionTools 组件显示
↓
用户操作 Section
↓
useSectionActions.xxx()
↓
DesignerCore 执行操作
↓
触发 change 事件关键设计模式
1. 单一职责原则
每个模块只负责一个功能:
DesignerCore- 编辑器核心逻辑useMaterial- 素材管理useSection- Section 状态
2. 依赖注入
通过 props 和参数传递依赖:
typescript
export function useSection(designerCore: DesignerCore) {
// designerCore 作为依赖注入
}3. 发布-订阅模式
事件系统实现组件通信:
typescript
designerCore.on('change', (content) => {
// 监听变化
})
designerCore.emit('change', content)4. 策略模式
可替换的加载器:
typescript
interface DesignerConfig {
categoryLoader?: () => Promise<MaterialCategory[]>
styleLoader?: (params) => Promise<StyleListData>
}扩展点
1. 自定义加载器
实现自定义素材加载逻辑:
typescript
const config = {
categoryLoader: async () => {
// 自定义实现
},
styleLoader: async (params) => {
// 自定义实现
}
}2. 自定义组件
替换默认组件:
vue
<template>
<Designer>
<template #material-panel>
<CustomMaterialPanel />
</template>
</Designer>
</template>3. 样式覆盖
通过 CSS 变量或类名覆盖:
css
.designer-container {
--primary-color: #42b983;
}性能优化
1. 懒加载
按需加载 UEditor 和素材:
typescript
// 只在需要时加载
const loadUEditor = async () => {
if (!window.UE) {
await loadUEditorScript()
}
}2. 虚拟滚动
素材列表使用虚拟滚动(计划中):
vue
<virtual-list :items="materials" :item-height="100">
<template #default="{ item }">
<MaterialItem :material="item" />
</template>
</virtual-list>3. 防抖节流
对频繁操作进行防抖:
typescript
const debouncedSave = debounce((content) => {
saveContent(content)
}, 1000)测试策略
单元测试
测试工具函数和 composables:
typescript
describe('StyleProcessor', () => {
it('should set width correctly', () => {
const element = document.createElement('div')
StyleProcessor.setWidth(element, 50)
expect(element.style.width).toBe('50%')
})
})组件测试
测试 Vue 组件:
typescript
describe('MaterialPanel', () => {
it('should render materials', async () => {
const wrapper = mount(MaterialPanel, {
props: { config }
})
await nextTick()
expect(wrapper.findAll('.material-item')).toHaveLength(10)
})
})E2E 测试
测试完整用户流程(计划中)。
未来规划
- [ ] 插件系统
- [ ] 更多内置素材模板
- [ ] 实时协作编辑
- [ ] 版本历史和撤销/重做
- [ ] 移动端优化