feat(view-state): 重构多楼层视图状态管理,提取场景加载逻辑并支持楼层独立的视图状态保存与恢复

This commit is contained in:
xudan 2025-12-17 11:01:15 +08:00
parent b1d77ae3c8
commit 972a63e738

View File

@ -64,6 +64,37 @@ const normalizeSceneJson = (raw: unknown): unknown => {
return result; return result;
}; };
const loadSceneFromJson = async (raw: unknown) => {
const sceneJson = normalizeSceneJson(raw);
// json
if (Array.isArray(sceneJson)) {
if (sceneJson.length > 0) {
//
floorScenes.value = sceneJson as any[];
currentFloorIndex.value = 0; //
await editor.value?.load(floorScenes.value[0]);
} else {
//
message.warn('场景文件为空数组');
floorScenes.value = [{}];
currentFloorIndex.value = 0;
await editor.value?.load('{}'); //
}
} else if (sceneJson && typeof sceneJson === 'object' && Object.keys(sceneJson as any).length > 0) {
//
floorScenes.value = [sceneJson];
currentFloorIndex.value = 0;
await editor.value?.load(sceneJson as any);
} else {
// null/undefined
message.warn('场景文件为空或格式不正确');
floorScenes.value = [{}];
currentFloorIndex.value = 0;
await editor.value?.load('{}'); //
}
};
//#region Playback Mode //#region Playback Mode
const mode = computed(() => (route.path.includes('/playback') ? 'playback' : 'live')); const mode = computed(() => (route.path.includes('/playback') ? 'playback' : 'live'));
const isPlaybackMode = computed(() => mode.value === 'playback'); const isPlaybackMode = computed(() => mode.value === 'playback');
@ -158,13 +189,16 @@ watch(playback.sceneJson, async (newJson) => {
if (newJson) { if (newJson) {
isSceneLoading.value = true; isSceneLoading.value = true;
try { try {
await editor.value?.load(newJson); await loadSceneFromJson(newJson);
// [] onFirstAmrData // [] onFirstAmrData
// await editor.value?.initRobots(); // await editor.value?.initRobots();
// [] // []
console.log('[回放模式] 构建电梯设备映射'); console.log('[回放模式] 构建电梯设备映射');
elevatorStore.refreshMapping(); elevatorStore.refreshMapping();
// load /
await handleAutoSaveAndRestoreViewState();
} finally { } finally {
isSceneLoading.value = false; isSceneLoading.value = false;
} }
@ -260,34 +294,7 @@ provide(EDITOR_KEY, editor);
const readScene = async () => { const readScene = async () => {
const res = props.id ? await getSceneByGroupId(props.id, props.sid) : await getSceneById(props.sid); const res = props.id ? await getSceneByGroupId(props.id, props.sid) : await getSceneById(props.sid);
title.value = res?.label ?? ''; title.value = res?.label ?? '';
const sceneJson = normalizeSceneJson(res?.json); await loadSceneFromJson(res?.json);
// json
if (Array.isArray(sceneJson)) {
if (sceneJson.length > 0) {
//
floorScenes.value = sceneJson as any[];
currentFloorIndex.value = 0; //
await editor.value?.load(floorScenes.value[0]);
} else {
//
message.warn('场景文件为空数组');
floorScenes.value = [{}];
currentFloorIndex.value = 0;
await editor.value?.load('{}'); //
}
} else if (sceneJson && typeof sceneJson === 'object' && Object.keys(sceneJson as any).length > 0) {
//
floorScenes.value = [sceneJson];
currentFloorIndex.value = 0;
await editor.value?.load(sceneJson as any);
} else {
// null/undefined
message.warn('场景文件为空或格式不正确');
floorScenes.value = [{}];
currentFloorIndex.value = 0;
await editor.value?.load('{}'); //
}
}; };
/** /**
@ -500,6 +507,9 @@ onMounted(async () => {
elevatorStore.refreshMapping(); elevatorStore.refreshMapping();
await monitorScene(); await monitorScene();
// readScene
await handleAutoSaveAndRestoreViewState();
} else { } else {
console.log('[回放模式] 初始化回放模式'); console.log('[回放模式] 初始化回放模式');
playback.connect(props.sid); playback.connect(props.sid);
@ -515,8 +525,6 @@ onMounted(async () => {
// await editor.value?.initRobots(); // await editor.value?.initRobots();
storageLocationService.value?.startMonitoring({ interval: 1 }); storageLocationService.value?.startMonitoring({ interval: 1 });
//
await handleAutoSaveAndRestoreViewState();
// //
if (editor.value) { if (editor.value) {
@ -705,7 +713,44 @@ const selectRobot = (id: string) => {
//#endregion //#endregion
//#region //#region
const { saveViewState, autoSaveAndRestoreViewState, isSaving, jumpToPosition, calculateCenterPoint } = useViewState(); const {
saveViewState,
restoreViewState,
autoSaveAndRestoreViewState,
isSaving,
jumpToPosition,
calculateCenterPoint,
} = useViewState();
const getViewStateSceneId = (): string => {
if (isMultiFloor.value) {
return `${props.sid}/${currentFloorIndex.value + 1}`;
}
return props.sid;
};
const getViewStateStorageKey = (sceneId: string, groupId?: string): string => {
return groupId ? `view-state-${groupId}-${sceneId}` : `view-state-${sceneId}`;
};
const migrateLegacyViewStateIfNeeded = () => {
if (!isMultiFloor.value) return;
try {
const groupId = props.id;
const legacyKey = getViewStateStorageKey(props.sid, groupId);
const floorKey = getViewStateStorageKey(getViewStateSceneId(), groupId);
if (legacyKey === floorKey) return;
const legacy = localStorage.getItem(legacyKey);
if (!legacy) return;
if (localStorage.getItem(floorKey)) return;
localStorage.setItem(floorKey, legacy);
} catch {
// ignore
}
};
/** /**
* 保存当前视图状态 * 保存当前视图状态
@ -714,7 +759,8 @@ const handleSaveViewState = async () => {
if (!editor.value) return; if (!editor.value) return;
try { try {
await saveViewState(editor.value, props.sid, props.id); migrateLegacyViewStateIfNeeded();
await saveViewState(editor.value, getViewStateSceneId(), props.id);
message.success('视图状态保存成功'); message.success('视图状态保存成功');
} catch { } catch {
message.error('视图状态保存失败'); message.error('视图状态保存失败');
@ -727,7 +773,8 @@ const handleSaveViewState = async () => {
const handleAutoSaveAndRestoreViewState = async () => { const handleAutoSaveAndRestoreViewState = async () => {
if (!editor.value) return; if (!editor.value) return;
await autoSaveAndRestoreViewState(editor.value, props.sid, props.id); migrateLegacyViewStateIfNeeded();
await autoSaveAndRestoreViewState(editor.value, getViewStateSceneId(), props.id);
}; };
//#endregion //#endregion
@ -735,13 +782,18 @@ const handleAutoSaveAndRestoreViewState = async () => {
const handleFloorChange = async (value: any) => { const handleFloorChange = async (value: any) => {
const newFloorIndex = value as number; const newFloorIndex = value as number;
if (editor.value && floorScenes.value[newFloorIndex]) { if (editor.value && floorScenes.value[newFloorIndex]) {
currentFloorIndex.value = newFloorIndex;
// 1) // 1)
await editor.value.load(floorScenes.value[newFloorIndex]); await editor.value.load(floorScenes.value[newFloorIndex]);
// 2) WS/ // 2) WS/
try { try {
migrateLegacyViewStateIfNeeded();
const restored = await restoreViewState(editor.value, getViewStateSceneId(), props.id);
if (!restored) {
const { centerX, centerY } = calculateCenterPoint(editor.value); const { centerX, centerY } = calculateCenterPoint(editor.value);
await jumpToPosition(editor.value, centerX, centerY, false, 0.05); await jumpToPosition(editor.value, centerX, centerY, false, 0.05);
}
} catch { } catch {
// ignore // ignore
} }