# 场景编辑器组件详细分析 ## 1. 组件概述 `scene-editor.vue` 是一个基于 Vue 3 的复杂场景编辑器组件,主要用于管理和编辑工业机器人的场景配置。该组件提供了完整的场景编辑功能,包括机器人管理、路径规划、区域设置等。 ## 2. 核心功能分析 ### 2.1 场景数据管理 - **场景读取**: 通过 `getSceneById` API 获取场景数据 - **场景推送**: 通过 `pushSceneById` API 将场景数据推送到数据库 - **场景保存**: 通过编辑器服务保存场景配置 - **文件导入/导出**: 支持 `.scene` 格式文件的导入导出 ### 2.2 编辑器状态控制 - **编辑模式切换**: 通过 `editable` 状态控制编辑器的启用/禁用 - **权限管理**: 根据编辑状态显示不同的操作按钮和功能 - **实时状态同步**: 编辑状态变化时自动更新编辑器服务状态 ### 2.3 三大管理区域 - **机器人管理**: 显示和管理场景中的机器人组和单个机器人 - **库区管理**: 管理各种类型的库区(仅显示库区类型的区域) - **高级组管理**: 管理复杂的路径、点位、区域等元素 ### 2.4 详情卡片系统 - **动态卡片显示**: 根据选中元素类型显示对应的详情卡片 - **编辑/查看模式**: 根据编辑状态显示编辑卡片或查看卡片 - **悬浮定位**: 卡片固定在右侧悬浮显示 ## 3. 技术架构分析 ### 3.1 核心依赖关系 ```typescript // 主要导入依赖 import { getSceneById, pushSceneById } from '@api/scene'; // 场景API import { EditorService } from '@core/editor.service'; // 编辑器服务 import { decodeTextFile, downloadFile, selectFile, textToBlob } from '@core/utils'; // 工具函数 ``` ### 3.2 组件架构设计 #### 3.2.1 状态管理 ```typescript // 核心状态定义 const title = ref(''); // 场景标题 const editable = ref(false); // 编辑状态 const show = ref(true); // 卡片显示状态 const current = ref<{ type: string; id: string }>(); // 当前选中元素 const container = shallowRef(); // 编辑器容器 const editor = shallowRef(); // 编辑器服务实例 ``` #### 3.2.2 依赖注入系统 ```typescript const EDITOR_KEY = Symbol('editor-key'); provide(EDITOR_KEY, editor); ``` 使用 Vue 3 的依赖注入机制,将编辑器服务注入到子组件中。 ### 3.3 EditorService 核心服务分析 #### 3.3.1 服务基础 ```typescript export class EditorService extends Meta2d { // 继承自 Meta2d 图形引擎 // 提供场景编辑的核心功能 } ``` #### 3.3.2 核心方法 - **load()**: 加载场景数据到编辑器 - **save()**: 保存当前场景数据 - **setState()**: 设置编辑器状态(可编辑/只读) - **updateRobots()**: 更新机器人数据 - **addArea()**: 添加区域 - **deleteById()**: 删除指定元素 ### 3.4 API 接口设计 #### 3.4.1 场景相关API ```typescript // 获取场景数据 export async function getSceneById(id: string): Promise; // 推送场景到数据库 export async function pushSceneById(id: string): Promise; // 保存场景数据 export async function saveSceneById(id: string, json: string, png?: string): Promise; ``` #### 3.4.2 文件操作工具 ```typescript // 文件选择 export async function selectFile(accept?: string, limit?: number): Promise; // 文件解码 export async function decodeTextFile(file: File): Promise; // 文本转二进制 export function textToBlob(text: string): Blob | undefined; // 文件下载 export function downloadFile(url: string, name?: string): void; ``` ## 4. 从零开发实现过程 ### 4.1 第一步:创建基础组件结构 ```vue ``` ### 4.2 第二步:集成编辑器服务 ```typescript // 1. 导入编辑器服务 import { EditorService } from '@core/editor.service'; // 2. 创建编辑器实例 const container = shallowRef(); const editor = shallowRef(); // 3. 组件挂载时初始化编辑器 onMounted(() => { editor.value = new EditorService(container.value!); }); // 4. 设置依赖注入 const EDITOR_KEY = Symbol('editor-key'); provide(EDITOR_KEY, editor); ``` ### 4.3 第三步:实现场景数据管理 ```typescript // 1. 导入API import { getSceneById, pushSceneById } from '@api/scene'; // 2. 读取场景数据 const readScene = async () => { const res = await getSceneById(props.id); title.value = res?.label ?? ''; editor.value?.load(res?.json, editable.value); }; // 3. 推送场景数据 const pushScene = async () => { const res = await pushSceneById(props.id); if (!res) return Promise.reject(); message.success(t('场景推送成功')); return Promise.resolve(); }; // 4. 监听场景ID变化 watch( () => props.id, () => readScene(), { immediate: true, flush: 'post' }, ); ``` ### 4.4 第四步:实现文件导入导出 ```typescript // 1. 导入工具函数 import { decodeTextFile, downloadFile, selectFile, textToBlob } from '@core/utils'; // 2. 导入场景文件 const importScene = async () => { const file = await selectFile('.scene'); if (!file?.size) return; const json = await decodeTextFile(file); editor.value?.load(json, editable.value); }; // 3. 导出场景文件 const exportScene = () => { const json = editor.value?.save(); if (!json) return; const blob = textToBlob(json); if (!blob?.size) return; const url = URL.createObjectURL(blob); downloadFile(url, `${title.value || 'unknown'}.scene`); URL.revokeObjectURL(url); }; ``` ### 4.5 第五步:集成管理组件 ```vue ``` ### 4.6 第六步:实现选中元素监听 ```typescript // 1. 监听编辑器选中元素 watch( () => editor.value?.selected.value[0], (v) => { const pen = editor.value?.getPenById(v); if (pen?.id) { current.value = { type: pen.name as 'point' | 'line' | 'area', id: pen.id }; return; } if (current.value?.type === 'robot') return; current.value = undefined; }, ); // 2. 计算选中元素类型 const isRobot = computed(() => current.value?.type === 'robot'); const isPoint = computed(() => current.value?.type === 'point'); const isRoute = computed(() => current.value?.type === 'line'); const isArea = computed(() => current.value?.type === 'area'); // 3. 机器人选择处理 const selectRobot = (id: string) => { current.value = { type: 'robot', id }; editor.value?.inactive(); }; ``` ### 4.7 第七步:添加工具栏和详情卡片 ```vue ``` ## 5. 子组件详细分析 ### 5.1 RobotGroups 组件 **功能**: 管理机器人组和单个机器人 **核心特性**: - 机器人组的增删改查 - 机器人的添加、注册、移除 - 批量操作支持(全选、批量移除) - 搜索过滤功能 **关键实现**: ```typescript // 机器人列表获取 const robots = computed(() => editor.value.robots.filter(({ label }) => label.includes(keyword.value))); // 批量选择管理 const selected = reactive>(new Set()); const selectAll = (checked: boolean) => { if (checked) { robots.value.forEach(({ id }) => selected.add(id)); } else { selected.clear(); } }; ``` ### 5.2 PenGroups 组件 **功能**: 管理点位、路线、区域等绘制元素 **核心特性**: - 分类显示不同类型的元素(点位、路线、区域) - 支持筛选特定类型(如仅显示库区) - 搜索过滤功能 - 点击选中功能 **关键实现**: ```typescript // 点位列表 const points = computed(() => editor.value.points.value.filter(({ label }) => label?.includes(keyword.value)), ); // 区域列表(按类型分组) const areas = computed(() => editor.value.areas.value.filter(({ label }) => label?.includes(keyword.value))); ``` ### 5.3 EditorToolbar 组件 **功能**: 提供编辑工具栏 **核心特性**: - 区域添加工具(库区、互斥区、非互斥区) - 场景保存功能 - 撤销/重做操作 - 删除操作 **关键实现**: ```typescript // 区域添加模式 const mode = ref(); watch(editor.value.mouseBrush, (v) => { if (!mode.value) return; const [p1, p2] = v ?? []; if (isEmpty(p1) || isEmpty(p2)) return; editor.value.addArea(p1, p2, mode.value); mode.value = undefined; }); ``` ## 6. 样式设计分析 ### 6.1 布局结构 - **头部**: 固定高度64px,包含标题和操作按钮 - **主体**: 左侧面板320px宽度,右侧编辑器自适应 - **工具栏**: 固定在底部中央,悬浮显示 - **详情卡片**: 固定在右侧,320px宽度,悬浮显示 ### 6.2 核心样式 ```scss .editor-container { background-color: transparent !important; } .toolbar-container { position: fixed; bottom: 40px; left: 50%; z-index: 100; transform: translateX(-50%); } .card-container { position: fixed; top: 80px; right: 64px; z-index: 100; width: 320px; height: calc(100% - 96px); overflow: visible; pointer-events: none; & > * { pointer-events: all; } } ``` ## 7. 维护和调试指南 ### 7.1 常见问题排查 #### 问题1: 场景数据加载失败 **排查步骤**: 1. 检查 `props.id` 是否正确传入 2. 检查 `getSceneById` API 是否正常响应 3. 检查编辑器服务是否正确初始化 #### 问题2: 编辑器功能异常 **排查步骤**: 1. 检查 `container` 元素是否正确获取 2. 检查 `EditorService` 是否正确实例化 3. 检查依赖注入是否正常工作 #### 问题3: 文件导入导出失败 **排查步骤**: 1. 检查工具函数是否正确导入 2. 检查文件格式是否正确 3. 检查浏览器兼容性 ### 7.2 性能优化建议 1. **使用 shallowRef**: 对于大对象使用 `shallowRef` 避免深度响应式 2. **组件懒加载**: 使用 `v-if` 控制组件渲染时机 3. **事件防抖**: 对于频繁触发的事件(如搜索)使用防抖 4. **内存管理**: 及时清理事件监听器和定时器 ### 7.3 扩展开发指南 #### 添加新的元素类型 1. 在 `EditorService` 中添加对应的管理方法 2. 在 `PenGroups` 组件中添加新的分组 3. 创建对应的详情卡片组件 4. 在主组件中添加类型判断逻辑 #### 添加新的工具 1. 在 `EditorToolbar` 组件中添加工具按钮 2. 在 `EditorService` 中实现对应功能 3. 处理工具状态管理和交互逻辑 ## 8. 总结 这个场景编辑器组件是一个功能完整、架构清晰的复杂组件,主要特点: 1. **模块化设计**: 通过子组件分离不同功能模块 2. **服务化架构**: 核心逻辑封装在 EditorService 中 3. **响应式状态管理**: 使用 Vue 3 的响应式系统管理复杂状态 4. **依赖注入**: 通过 provide/inject 实现服务共享 5. **文件操作**: 完整的文件导入导出功能 6. **用户体验**: 良好的交互设计和视觉反馈 对于维护和扩展这个组件,需要重点关注: - EditorService 的 API 设计和实现 - 各子组件之间的通信机制 - 状态管理的一致性 - 性能优化和内存管理