diff --git a/src/pages/scene-editor.vue b/src/pages/scene-editor.vue index 699b7c8..7111fa1 100644 --- a/src/pages/scene-editor.vue +++ b/src/pages/scene-editor.vue @@ -256,18 +256,139 @@ const toPush = () => { }; // --- Import/Update Logic --- -const importScene = async () => { - const file = await selectFile('.scene'); - if (!file) return; - const jsonString = await decodeTextFile(file); - if (!jsonString) { - message.error('文件内容为空或无法读取'); - return; +const importModalVisible = ref(false); +const importMode = ref<'normal' | 'floor'>('normal'); +const normalImportFileList = ref([]); +const normalImportKeepProperties = ref(true); +const floorImportList = ref([{ name: 'F1', fileList: [], keepProperties: true }]); + +const fileTableColumns = [ + { title: '文件名', dataIndex: 'name', key: 'name' }, + { title: '格式', dataIndex: 'format', key: 'format', width: 100 }, + { title: '文件大小', dataIndex: 'size', key: 'size', width: 120 }, +]; + +const handleNormalFileChange = (info: any) => { + // 限制只上传一个文件 + let fileList = [...info.fileList]; + fileList = fileList.slice(-1); + normalImportFileList.value = fileList; +}; + +const handleFloorFileChange = (info: any, index: number) => { + floorImportList.value[index].fileList = [...info.fileList]; +}; + +const addFloor = () => { + floorImportList.value.push({ + name: `F${floorImportList.value.length + 1}`, + fileList: [], + keepProperties: true, + }); +}; + +const mergeSceneObjects = (scenes: any[]): any => { + if (!scenes || scenes.length === 0) { + return {}; } - try { - const sceneData = JSON.parse(jsonString); + return scenes.reduce((merged, currentScene) => { + const newMerged = { ...merged, ...currentScene }; + if (currentScene.pens && Array.isArray(currentScene.pens)) { + const penMap = new Map(merged.pens?.map((p: any) => [p.id, p]) ?? []); + currentScene.pens.forEach((pen: any) => { + if (pen.id) { + penMap.set(pen.id, pen); + } + }); + newMerged.pens = Array.from(penMap.values()); + } + return newMerged; + }, {}); +}; +const openImportModal = () => { + importMode.value = 'normal'; + importModalVisible.value = true; +}; + +const handleImportConfirm = async () => { + try { + if (importMode.value === 'normal') { + if (normalImportFileList.value.length === 0) { + message.warn('请选择一个文件'); + return; + } + const file = normalImportFileList.value[0].originFileObj as File; + const fileName = file.name.toLowerCase(); + let sceneJsonString: string | null = null; + + if (fileName.endsWith('.smap')) { + // SMAP 文件转换逻辑 + if (normalImportKeepProperties.value) { + // 更新模式 + const currentSceneJson = editor.value?.save(); + 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', + }); + sceneJsonString = await convertSmapToScene(file, sceneFile); + } else { + // 创建模式 + sceneJsonString = await convertSmapToScene(file); + } + } else if (fileName.endsWith('.scene')) { + // SCENE 文件直接读取 + sceneJsonString = await decodeTextFile(file); + } else { + message.error('不支持的文件格式。请选择 .scene 或 .smap 文件。'); + return; + } + + if (!sceneJsonString) { + message.error('文件内容为空或转换失败'); + return; + } + await processAndLoadSceneData(JSON.parse(sceneJsonString)); + } else if (importMode.value === 'floor') { + if (floorImportList.value.some((f) => f.fileList.length === 0)) { + message.warn('每个楼层都需要选择至少一个文件'); + return; + } + const floorJsons = await Promise.all( + floorImportList.value.map(async (floor) => { + const sceneObjectsForFloor = await Promise.all( + floor.fileList.map(async (fileInfo: any) => { + const file = fileInfo.originFileObj as File; + const jsonString = await decodeTextFile(file); + if (!jsonString) { + throw new Error(`楼层 ${floor.name} 中的文件 ${file.name} 为空`); + } + return JSON.parse(jsonString); + }), + ); + return mergeSceneObjects(sceneObjectsForFloor); + }), + ); + await processAndLoadSceneData(floorJsons); + } + + importModalVisible.value = false; + // 清理状态 + normalImportFileList.value = []; + floorImportList.value = [{ name: 'F1', fileList: [], keepProperties: true }]; + } catch (error) { + console.error('导入失败:', error); + message.error(`导入失败: ${(error as Error).message || '请检查文件格式或内容。'}`); + } +}; + +const processAndLoadSceneData = async (sceneData: any) => { + try { // 检查导入的数据是数组(多楼层)还是对象(单楼层) if (Array.isArray(sceneData)) { requireImportTransform.value = true; @@ -304,8 +425,10 @@ const importScene = async () => { await editor.value?.load('{}', editable.value, undefined, requireImportTransform.value); } } catch (error) { - console.error('场景文件解析失败:', error); - message.error('导入失败:文件不是有效的JSON格式。'); + console.error('场景文件解析或加载失败:', error); + message.error('导入失败:文件不是有效的JSON格式或数据无法加载。'); + // 抛出错误以确保上层可以捕获 + throw error; } }; @@ -489,7 +612,7 @@ const handleFloorChange = async (value: any) => { {{ $t('推送') }} - + {{ $t('导入') }}