Skip to content

项目架构

本文档介绍 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 测试

测试完整用户流程(计划中)。

未来规划

  • [ ] 插件系统
  • [ ] 更多内置素材模板
  • [ ] 实时协作编辑
  • [ ] 版本历史和撤销/重做
  • [ ] 移动端优化

相关文档

Released under the Apache License 2.0. 压缩版免费使用,商用或源码需授权。