fix(scene-editor): 优化场景加载逻辑,增加导入状态管理,改善用户体验

This commit is contained in:
xudan 2025-10-27 16:59:06 +08:00
parent f7cd51c679
commit 85f7b94313

View File

@ -60,43 +60,48 @@ const normalizeSceneJson = (raw: unknown): unknown => {
//#region //#region
const readScene = async () => { const readScene = async () => {
const res = await getSceneById(props.id); isImporting.value = true;
title.value = res?.label ?? ''; try {
const sceneJson = normalizeSceneJson(res?.json); const res = await getSceneById(props.id);
title.value = res?.label ?? '';
const sceneJson = normalizeSceneJson(res?.json);
requireImportTransform.value = false; requireImportTransform.value = false;
// //
if (Array.isArray(sceneJson)) { if (Array.isArray(sceneJson)) {
if (sceneJson.length > 0) { if (sceneJson.length > 0) {
// //
floorScenes.value = sceneJson; floorScenes.value = sceneJson;
floorImportFlags.value = new Array(sceneJson.length).fill(false); floorImportFlags.value = new Array(sceneJson.length).fill(false);
currentFloorIndex.value = 0;
previousFloorIndex.value = 0;
editor.value?.load(floorScenes.value[0], editable.value, undefined, floorImportFlags.value[0] ?? false);
} else {
//
message.warn('场景文件为空数组');
floorScenes.value = [{}]; //
floorImportFlags.value = [false];
currentFloorIndex.value = 0;
previousFloorIndex.value = 0;
editor.value?.load('{}', editable.value, undefined, false);
}
} else if (sceneJson && typeof sceneJson === 'object' && Object.keys(sceneJson).length > 0) {
//
floorScenes.value = [sceneJson];
floorImportFlags.value = [false];
currentFloorIndex.value = 0; currentFloorIndex.value = 0;
previousFloorIndex.value = 0; previousFloorIndex.value = 0;
editor.value?.load(floorScenes.value[0], editable.value, undefined, floorImportFlags.value[0] ?? false); editor.value?.load(floorScenes.value[0], editable.value, undefined, floorImportFlags.value[0] ?? false);
} else { } else {
// message.warn('场景文件为空或格式不正确');
message.warn('场景文件为空数组');
floorScenes.value = [{}]; // floorScenes.value = [{}]; //
floorImportFlags.value = [false]; floorImportFlags.value = [false];
currentFloorIndex.value = 0; currentFloorIndex.value = 0;
previousFloorIndex.value = 0; previousFloorIndex.value = 0;
editor.value?.load('{}', editable.value, undefined, false); editor.value?.load('{}', editable.value, undefined, false);
} }
} else if (sceneJson && typeof sceneJson === 'object' && Object.keys(sceneJson).length > 0) { } finally {
// isImporting.value = false;
floorScenes.value = [sceneJson];
floorImportFlags.value = [false];
currentFloorIndex.value = 0;
previousFloorIndex.value = 0;
editor.value?.load(floorScenes.value[0], editable.value, undefined, floorImportFlags.value[0] ?? false);
} else {
message.warn('场景文件为空或格式不正确');
floorScenes.value = [{}]; //
floorImportFlags.value = [false];
currentFloorIndex.value = 0;
previousFloorIndex.value = 0;
editor.value?.load('{}', editable.value, undefined, false);
} }
}; };
@ -208,6 +213,8 @@ const isMultiFloor = computed(() => floorScenes.value.length > 1);
const requireImportTransform = ref(false); const requireImportTransform = ref(false);
// floorScenes // floorScenes
const floorImportFlags = ref<boolean[]>([]); const floorImportFlags = ref<boolean[]>([]);
//
const isImporting = ref(false);
onMounted(() => { onMounted(() => {
document.title = '场景编辑器'; document.title = '场景编辑器';
@ -345,6 +352,7 @@ const openImportModal = () => {
}; };
const handleImportConfirm = async () => { const handleImportConfirm = async () => {
isImporting.value = true;
try { try {
/* NORMAL_IMPORT_DISABLED /* NORMAL_IMPORT_DISABLED
if (importMode.value === 'normal') { if (importMode.value === 'normal') {
@ -417,7 +425,7 @@ const handleImportConfirm = async () => {
console.log('[MapConverter] OriginalProperties by floor:', OriginalProperties.value); console.log('[MapConverter] OriginalProperties by floor:', OriginalProperties.value);
console.log('[MapConverter] Request payload (object):', payload); console.log('[MapConverter] Request payload (object):', payload);
console.log('[MapConverter] Request payload (JSON):', JSON.stringify(payload, null, 2)); console.log('[MapConverter] Request payload (JSON):', JSON.stringify(payload, null, 2));
message.info('已在控制台打印请求参数'); // message.info('');
} catch { } catch {
// //
} }
@ -496,10 +504,13 @@ const handleImportConfirm = async () => {
} catch (error) { } catch (error) {
console.error('导入失败:', error); console.error('导入失败:', error);
message.error(`导入失败: ${(error as Error).message || '请检查文件格式或内容。'}`); message.error(`导入失败: ${(error as Error).message || '请检查文件格式或内容。'}`);
} finally {
isImporting.value = false;
} }
}; };
const processAndLoadSceneData = async (sceneData: any) => { const processAndLoadSceneData = async (sceneData: any) => {
isImporting.value = true;
try { try {
// //
if (Array.isArray(sceneData)) { if (Array.isArray(sceneData)) {
@ -552,33 +563,43 @@ const processAndLoadSceneData = async (sceneData: any) => {
message.error('导入失败文件不是有效的JSON格式或数据无法加载。'); message.error('导入失败文件不是有效的JSON格式或数据无法加载。');
// //
throw error; throw error;
} finally {
isImporting.value = false;
} }
}; };
const importSmapModalVisible = ref(false); const importSmapModalVisible = ref(false);
const handleImportSmapConfirm = async ({ smapFile, keepProperties }: { smapFile: File; keepProperties: boolean }) => { const handleImportSmapConfirm = async ({ smapFile, keepProperties }: { smapFile: File; keepProperties: boolean }) => {
let sceneJson: string | null = null; isImporting.value = true;
if (keepProperties) { try {
// Update mode let sceneJson: string | null = null;
const currentSceneJson = editor.value?.save(); if (keepProperties) {
if (!currentSceneJson) { // Update mode
message.error('无法获取当前场景数据,请确保场景不为空'); const currentSceneJson = editor.value?.save();
return; if (!currentSceneJson) {
message.error('无法获取当前场景数据,请确保场景不为空');
return;
}
const sceneBlob = new Blob([currentSceneJson], { type: 'application/json' });
const sceneFile = new File([sceneBlob], `${title.value || 'current'}.scene`, {
type: 'application/json',
});
sceneJson = await convertSmapToScene(smapFile, sceneFile);
} else {
// Create mode
sceneJson = await convertSmapToScene(smapFile);
} }
const sceneBlob = new Blob([currentSceneJson], { type: 'application/json' });
const sceneFile = new File([sceneBlob], `${title.value || 'current'}.scene`, {
type: 'application/json',
});
sceneJson = await convertSmapToScene(smapFile, sceneFile);
} else {
// Create mode
sceneJson = await convertSmapToScene(smapFile);
}
if (sceneJson) { if (sceneJson) {
requireImportTransform.value = true; requireImportTransform.value = true;
editor.value?.load(sceneJson, editable.value, undefined, requireImportTransform.value); editor.value?.load(sceneJson, editable.value, undefined, requireImportTransform.value);
}
} catch (error) {
console.error('导入 SMAP 文件失败:', error);
message.error('导入 SMAP 文件失败');
} finally {
isImporting.value = false;
} }
}; };
@ -748,7 +769,7 @@ const handleFloorChange = async (value: any) => {
</a-button> </a-button>
<a-button @click="toPush">{{ $t('推送') }}</a-button> <a-button @click="toPush">{{ $t('推送') }}</a-button>
<a-dropdown-button @click="openImportModal" :loading="isConverting"> <a-dropdown-button @click="openImportModal" :loading="isConverting || isImporting">
{{ $t('导入') }} {{ $t('导入') }}
<template #overlay> <template #overlay>
<a-menu> <a-menu>
@ -929,7 +950,7 @@ const handleFloorChange = async (value: any) => {
<span class="hint-text">* 多文件仅支持 smap请确保 floors 连续且类型与后缀一致</span> <span class="hint-text">* 多文件仅支持 smap请确保 floors 连续且类型与后缀一致</span>
<a-space> <a-space>
<a-button @click="importModalVisible = false">取消</a-button> <a-button @click="importModalVisible = false">取消</a-button>
<a-button type="primary" @click="handleImportConfirm">导入</a-button> <a-button type="primary" @click="handleImportConfirm" :loading="isImporting">导入</a-button>
</a-space> </a-space>
</div> </div>
</div> </div>
@ -1079,6 +1100,7 @@ const handleFloorChange = async (value: any) => {
padding: 16px; padding: 16px;
border-radius: 4px; border-radius: 4px;
margin-bottom: 16px; margin-bottom: 16px;
&:last-child { &:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
@ -1087,5 +1109,8 @@ html[theme='dark'] {
.sider-toggle { .sider-toggle {
background-color: #5a5d5e; background-color: #5a5d5e;
} }
.floor-uploader-item {
border: 1px solid #141414;
}
} }
</style> </style>