From 7374ffd6ff0b9fa4bba0e7621be2105abc389cff Mon Sep 17 00:00:00 2001 From: xudan Date: Wed, 2 Jul 2025 09:27:40 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E8=A7=86=E5=9B=BE?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E7=AE=A1=E7=90=86=EF=BC=8C=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E4=BF=9D=E5=AD=98=E4=B8=8E=E6=81=A2=E5=A4=8D?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=8F=90=E5=8D=87=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BD=93=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/movement-supervision.vue | 16 +- src/pages/scene-editor.vue | 16 +- src/services/useViewState.ts | 269 +++++++++++++++-------------- vite.config.ts | 3 +- 4 files changed, 158 insertions(+), 146 deletions(-) diff --git a/src/pages/movement-supervision.vue b/src/pages/movement-supervision.vue index 6b3e7fe..6fbb7a5 100644 --- a/src/pages/movement-supervision.vue +++ b/src/pages/movement-supervision.vue @@ -54,8 +54,8 @@ onMounted(async () => { await readScene(); await editor.value?.initRobots(); await monitorScene(); - // 自动恢复视图状态 - await handleRestoreViewState(); + // 自动保存和恢复视图状态 + await handleAutoSaveAndRestoreViewState(); }); onUnmounted(() => { client.value?.close(); @@ -86,7 +86,7 @@ const selectRobot = (id: string) => { }; // 视图状态管理 -const { saveViewState, restoreViewState, isSaving } = useViewState(); +const { saveViewState, autoSaveAndRestoreViewState, isSaving } = useViewState(); // 保存当前视图状态 const handleSaveViewState = async () => { @@ -100,15 +100,11 @@ const handleSaveViewState = async () => { } }; -// 恢复视图状态 -const handleRestoreViewState = async () => { +// 自动保存和恢复视图状态 +const handleAutoSaveAndRestoreViewState = async () => { if (!editor.value) return; - const restored = await restoreViewState(editor.value, props.sid, props.id); - if (!restored) { - // 如果没有保存的状态,使用默认的centerView - editor.value.centerView(); - } + await autoSaveAndRestoreViewState(editor.value, props.sid, props.id); }; diff --git a/src/pages/scene-editor.vue b/src/pages/scene-editor.vue index 172c0c3..1f87509 100644 --- a/src/pages/scene-editor.vue +++ b/src/pages/scene-editor.vue @@ -55,8 +55,8 @@ watch( () => props.id, async () => { await readScene(); - // 在场景加载完成后恢复视图状态 - await handleRestoreViewState(); + // 在场景加载完成后自动保存和恢复视图状态 + await handleAutoSaveAndRestoreViewState(); }, { immediate: true, flush: 'post' }, ); @@ -131,7 +131,7 @@ const selectRobot = (id: string) => { }; // 视图状态管理 -const { saveViewState, restoreViewState, isSaving } = useViewState(); +const { saveViewState, autoSaveAndRestoreViewState, isSaving } = useViewState(); // 保存当前视图状态 const handleSaveViewState = async () => { @@ -145,15 +145,11 @@ const handleSaveViewState = async () => { } }; -// 恢复视图状态 -const handleRestoreViewState = async () => { +// 自动保存和恢复视图状态 +const handleAutoSaveAndRestoreViewState = async () => { if (!editor.value) return; - const restored = await restoreViewState(editor.value, props.id); - if (!restored) { - // 如果没有保存的状态,使用默认的centerView - editor.value.centerView(); - } + await autoSaveAndRestoreViewState(editor.value, props.id); }; diff --git a/src/services/useViewState.ts b/src/services/useViewState.ts index ca084ea..dfcbc3d 100644 --- a/src/services/useViewState.ts +++ b/src/services/useViewState.ts @@ -38,78 +38,54 @@ export function useViewState() { * @param editor 编辑器服务实例 */ const getCurrentViewState = (editor: EditorService): ViewState => { - console.groupCollapsed('🔍 [getCurrentViewState] - 获取当前视图状态'); + // 获取当前缩放比例和中心点坐标 + const scale = editor.store.data.scale || 1; + const { centerX, centerY } = calculateCenterPoint(editor); + const finalState: ViewState = { + scale, + centerX, + centerY, + timestamp: Date.now(), + }; + + return finalState; + }; + + /** + * 计算画布中心点坐标 + * @param editor 编辑器服务实例 + */ + const calculateCenterPoint = (editor: EditorService): { centerX: number; centerY: number } => { // 1. 查找画布上所有的点位 const points = editor.find('point'); - console.log('📍 画布上的点位数量:', points.length); - // 打印所有点位的原始数据,以便检查 - console.log( - '📊 点位原始数据:', - points.map((point) => ({ - id: point.id, - x: point.x, - y: point.y, - name: point.name, - text: point.text, - })), - ); + // 如果没有点位,返回当前视图中心 + if (!points.length) { + return { + centerX: 0, + centerY: 0, + }; + } - // 2. 计算所有点位的边界 + // 2. 获取场景数据 + const sceneData = getSceneData(editor); + const { ratio = 1, width: mapWidth = 0, height: mapHeight = 0 } = sceneData; + + // 3. 计算所有点位的边界 let minX = Infinity; let minY = Infinity; let maxX = -Infinity; let maxY = -Infinity; - // 尝试获取编辑器中的原始场景数据 - // 编辑器中的#originalSceneData是Partial类型 - type SceneDataType = { ratio?: number; width?: number; height?: number }; - let originalSceneData: SceneDataType = { ratio: 1, width: 0, height: 0 }; - - try { - // 尝试通过编辑器的save方法获取场景数据 - const sceneJson = editor.save(); - if (sceneJson) { - const sceneData = JSON.parse(sceneJson); - // 提取需要的字段 - originalSceneData = { - ratio: sceneData.ratio || 1, - width: sceneData.width || 0, - height: sceneData.height || 0, - }; - console.log('🗺️ 从场景数据中提取的信息:', originalSceneData); - } - } catch (error) { - console.error('获取编辑器原始数据失败:', error); - console.log('⚠️ 使用默认值'); - } - - const ratio = originalSceneData?.ratio || 1; - const mapWidth = originalSceneData?.width || 0; - const mapHeight = originalSceneData?.height || 0; - - console.log('🗺️ 地图信息:', { ratio, mapWidth, mapHeight }); - points.forEach((point) => { // 获取点位的实际坐标(画布上显示的坐标) const { x: displayX = 0, y: displayY = 0 } = editor.getPointRect(point) ?? {}; // 将显示坐标转换回原始坐标系统 - // 这里我们尝试逆转编辑器中的坐标转换逻辑 - // 根据editor.service.ts中的#transformCoordinate方法 - - // 1. 从显示坐标转回中心点原点坐标 - const centerX = displayX; - const centerY = displayY; - - // 2. 从中心点原点转回左上角原点坐标 - const topLeftX = (centerX + mapWidth / 2) * ratio; - const topLeftY = (mapHeight / 2 - centerY) * ratio; - - console.log( - `点位 ${point.id || point.name || '未命名'}: 显示坐标(${displayX}, ${displayY}), 原始坐标(${topLeftX.toFixed(1)}, ${topLeftY.toFixed(1)})`, - ); + // 1. 从中心点原点转回左上角原点坐标 + const topLeftX = (displayX + mapWidth / 2) * ratio; + const topLeftY = (mapHeight / 2 - displayY) * ratio; // 使用原始坐标系统的坐标计算边界 minX = Math.min(minX, topLeftX); @@ -118,22 +94,19 @@ export function useViewState() { maxY = Math.max(maxY, topLeftY); }); - // 如果没有找到任何点位,使用默认值 + // 如果没有有效的边界,返回原点 if (minX === Infinity || minY === Infinity || maxX === -Infinity || maxY === -Infinity) { - minX = 0; - minY = 0; - maxX = 0; - maxY = 0; + return { + centerX: 0, + centerY: 0, + }; } - // 3. 计算边界的中心点(在原始坐标系统中) + // 4. 计算边界的中心点(在原始坐标系统中) const centerX = (minX + maxX) / 2; const centerY = (minY + maxY) / 2; - console.log('🔍 原始坐标系中的点位边界:', { minX, minY, maxX, maxY }); - console.log('🎯 原始坐标系中的中心点:', { centerX, centerY }); - - // 4. 将中心点从原始坐标系统转换回编辑器的显示坐标系统 + // 5. 将中心点从原始坐标系统转换回编辑器的显示坐标系统 // 先根据ratio进行缩放 const scaledCenterX = centerX / ratio; const scaledCenterY = centerY / ratio; @@ -142,21 +115,33 @@ export function useViewState() { const displayCenterX = scaledCenterX - mapWidth / 2; const displayCenterY = mapHeight / 2 - scaledCenterY; - console.log('🎯 显示坐标系中的中心点:', { x: displayCenterX, y: displayCenterY }); - console.log('⚙️ 当前画布状态 (origin, scale):', { - origin: JSON.parse(JSON.stringify(editor.store.data.origin)), - scale: editor.store.data.scale, - }); - - const finalState: ViewState = { - scale: editor.store.data.scale || 1, + return { centerX: displayCenterX, centerY: displayCenterY, - timestamp: Date.now(), }; - console.log('💾 保存的最终视图状态:', finalState); - console.groupEnd(); - return finalState; + }; + + /** + * 获取场景数据 + * @param editor 编辑器服务实例 + */ + const getSceneData = (editor: EditorService): { ratio: number; width: number; height: number } => { + try { + const sceneJson = editor.save(); + if (sceneJson) { + const sceneData = JSON.parse(sceneJson); + return { + ratio: sceneData.ratio || 1, + width: sceneData.width || 0, + height: sceneData.height || 0, + }; + } + } catch (error) { + console.error('获取编辑器原始数据失败:', error); + } + + // 返回默认值 + return { ratio: 1, width: 0, height: 0 }; }; /** @@ -186,78 +171,70 @@ export function useViewState() { * @param groupId 群组ID (可选) */ const restoreViewState = async (editor: EditorService, sceneId: string, groupId?: string): Promise => { - console.groupCollapsed('🔄 [restoreViewState] - 恢复视图状态'); isRestoring.value = true; try { const storageKey = getStorageKey(sceneId, groupId); const savedState = localStorage.getItem(storageKey); if (!savedState) { - console.warn('⚠️ 未找到保存的视图状态'); - console.groupEnd(); return false; } const viewState: ViewState = JSON.parse(savedState); - console.log('💾 读取到的视图状态:', viewState); // 1. 设置缩放比例 editor.scale(viewState.scale); - console.log(`🔎 1. 设置缩放比例为: ${viewState.scale}`); - // 2. 尝试跳转到画布上的一个随机点位 + // 2. 如果有中心点坐标,创建临时点位并跳转 if (viewState.centerX !== undefined && viewState.centerY !== undefined) { - try { - // 查找画布上所有的点位 - const points = editor.find('point'); - console.log('📍 画布上的点位数量:', points.length); - - // 如果没有找到点位或跳转失败,创建一个临时点位 - console.log('⚠️ 创建临时点位进行跳转'); - const centerPointId = 'view-center-point-' + Date.now(); - - // 使用 addPoint 方法添加临时点位 - // 这个方法会处理坐标转换和其他必要的设置 - await editor.addPoint( - { x: viewState.centerX, y: viewState.centerY }, // 点位坐标 - 1, // 点位类型:普通点 - centerPointId, // 点位ID - ); - - // 设置点位为不可见 - editor.setValue({ id: centerPointId, visible: false }, { render: false, history: false, doEvent: false }); - - // 使用 gotoById 方法跳转到临时点 - editor.gotoById(centerPointId); - console.log('🎯 使用 gotoById 跳转到临时点:', { x: viewState.centerX, y: viewState.centerY }); - - // 延迟一段时间后移除临时点 - setTimeout(() => { - const tempPen = editor.getPenById(centerPointId); - if (tempPen) { - editor.delete([tempPen]); - } - }, 100); - } catch (error) { - console.error('使用 gotoById 跳转失败:', error); - } + await jumpToPosition(editor, viewState.centerX, viewState.centerY); } - // 3. 触发重绘 - // editor.render(); - console.log('🎨 3. 重绘画布完成'); - - console.groupEnd(); return true; } catch (error) { console.error('恢复视图状态失败:', error); - console.groupEnd(); return false; } finally { isRestoring.value = false; } }; + /** + * 跳转到指定位置 + * @param editor 编辑器服务实例 + * @param x X坐标 + * @param y Y坐标 + */ + const jumpToPosition = async (editor: EditorService, x: number, y: number): Promise => { + try { + // 创建临时点位用于跳转 + const centerPointId = 'view-center-point-' + Date.now(); + + // 添加临时点位 + await editor.addPoint( + { x, y }, // 点位坐标 + 1, // 点位类型:普通点 + centerPointId, // 点位ID + ); + + // 设置点位为不可见 + editor.setValue({ id: centerPointId, visible: false }, { render: false, history: false, doEvent: false }); + + // 跳转到临时点 + editor.gotoById(centerPointId); + + // 延迟移除临时点 + setTimeout(() => { + const tempPen = editor.getPenById(centerPointId); + if (tempPen) { + editor.delete([tempPen]); + } + }, 100); + } catch (error) { + console.error('跳转到指定位置失败:', error); + } + }; + /** * 检查是否存在保存的视图状态 * @param sceneId 场景ID @@ -293,6 +270,47 @@ export function useViewState() { } }; + /** + * 自动保存和恢复视图状态 + * 如果本地没有保存的key,则先保存当前状态(用于定位地图),然后恢复 + * 如果有key,则直接按原来逻辑恢复 + * @param editor 编辑器服务实例 + * @param sceneId 场景ID + * @param groupId 群组ID (可选) + */ + const autoSaveAndRestoreViewState = async ( + editor: EditorService, + sceneId: string, + groupId?: string, + ): Promise => { + try { + const hasExistingState = hasViewState(sceneId, groupId); + + if (!hasExistingState) { + // 先让编辑器居中显示,这样可以获得一个合适的默认视图状态 + editor.centerView(); + + // 等待一小段时间让centerView完成 + await new Promise((resolve) => setTimeout(resolve, 100)); + + // 保存当前状态作为默认状态 + await saveViewState(editor, sceneId, groupId); + + // 然后恢复这个刚保存的状态(主要是为了触发定位逻辑) + return await restoreViewState(editor, sceneId, groupId); + } else { + // 直接恢复已有状态 + return await restoreViewState(editor, sceneId, groupId); + } + } catch (error) { + console.error('自动保存和恢复视图状态失败:', error); + + // 如果出错,至少执行centerView作为fallback + editor.centerView(); + return false; + } + }; + return { isSaving, isRestoring, @@ -302,5 +320,6 @@ export function useViewState() { clearViewState, getViewStateInfo, getCurrentViewState, + autoSaveAndRestoreViewState, }; } diff --git a/vite.config.ts b/vite.config.ts index 9bcb965..00396a2 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -37,7 +37,8 @@ export default ({ mode }: Record) => chunkSizeWarningLimit: 2000, }, esbuild: { - drop: mode === 'production' ? ['console'] : [], + drop: mode === 'production' ? [] : [], + // drop: mode === 'production' ? [] : [], }, server: { port: 8888,