diff --git a/src/hooks/useMapConversion.ts b/src/hooks/useMapConversion.ts index 1a83e7e..a250197 100644 --- a/src/hooks/useMapConversion.ts +++ b/src/hooks/useMapConversion.ts @@ -7,30 +7,42 @@ import { downloadFile } from '../services/utils'; const vwedApi = '/vwedApi'; const API_BASE_URL = '' + vwedApi + '/api/vwed-map-converter'; -// Create a new Axios instance specifically for map conversion APIs -// to avoid conflicts with the global instance's baseURL. const mapConverterHttp = axios.create(); export function useMapConversion() { const isConverting = ref(false); - const convertSmapToScene = async (smapFile: File): Promise => { + /** + * Imports or updates a Scene using an SMAP file. + * Calls the smap-to-scene API endpoint. + * @param smapFile The .smap file. + * @param sceneFile Optional .scene file for updates. If not provided, a new scene is created. + * @returns The resulting scene JSON string, or null if failed. + */ + const convertSmapToScene = async (smapFile: File, sceneFile?: File): Promise => { isConverting.value = true; + const isUpdate = !!sceneFile; + const actionText = isUpdate ? '更新' : '新建'; + try { const formData = new FormData(); formData.append('smap_file', smapFile); + if (sceneFile) { + formData.append('scene_file', sceneFile); + } + const response = await mapConverterHttp.post(`${API_BASE_URL}/smap-to-scene`, formData, { headers: { 'Content-Type': 'multipart/form-data' }, }); - // Note: axios wraps the response in a `data` object. + if (response.data.code === 200 || response.data.success) { - message.success('SMAP 转换为 Scene 成功!'); + message.success(`通过 SMAP ${actionText} Scene 成功!`); return JSON.stringify(response.data.data, null, 2); } else { - throw new Error(response.data.message || '转换失败'); + throw new Error(response.data.message || `${actionText}失败`); } } catch (error: any) { - const errorMessage = error.response?.data?.message || error.message || 'SMAP 转换为 Scene 失败'; + const errorMessage = error.response?.data?.message || error.message || `通过 SMAP ${actionText} Scene 失败`; message.error(errorMessage); return null; } finally { @@ -38,12 +50,20 @@ export function useMapConversion() { } }; - const convertSceneToSmap = async (sceneJson: string, filename: string) => { + /** + * Exports a Scene to an SMAP file format. + * Requires both a scene and a base smap file. + * Calls the scene-to-smap API endpoint. + * @param sceneFile The .scene file to export. + * @param smapFile The base .smap file to update. + * @param filename The base name for the downloaded file. + */ + const exportSceneToSmap = async (sceneFile: File, smapFile: File, filename: string) => { isConverting.value = true; try { const formData = new FormData(); - const sceneBlob = new Blob([sceneJson], { type: 'application/json' }); - formData.append('scene_file', sceneBlob, `${filename}.scene`); + formData.append('scene_file', sceneFile); + formData.append('smap_file', smapFile); const response = await mapConverterHttp.post(`${API_BASE_URL}/scene-to-smap`, formData, { headers: { 'Content-Type': 'multipart/form-data' }, @@ -58,12 +78,12 @@ export function useMapConversion() { : `${filename}.smap`; downloadFile(url, outputFilename); URL.revokeObjectURL(url); - message.success('Scene 转换为 SMAP 并下载成功!'); + message.success('导出到 SMAP 文件成功!'); } else { - throw new Error(response.data.message || '转换失败'); + throw new Error(response.data.message || '导出失败'); } } catch (error: any) { - const errorMessage = error.response?.data?.message || error.message || 'Scene 转换为 SMAP 失败'; + const errorMessage = error.response?.data?.message || error.message || '导出到 SMAP 文件失败'; message.error(errorMessage); } finally { isConverting.value = false; @@ -88,7 +108,7 @@ export function useMapConversion() { } const disposition = response?.headers?.['content-disposition'] || ''; - let downloadFilename = `${filename}.zip`; // Default + let downloadFilename = `${filename}.zip`; const filenameMatch = disposition.match(/filename=([^;]+)/i); if (filenameMatch && filenameMatch[1]) { downloadFilename = decodeURIComponent(filenameMatch[1].replace(/"/g, '')); @@ -109,7 +129,7 @@ export function useMapConversion() { return { isConverting, convertSmapToScene, - convertSceneToSmap, + exportSceneToSmap, convertSceneToIray, }; } diff --git a/src/pages/scene-editor.vue b/src/pages/scene-editor.vue index 58df98f..11e8b62 100644 --- a/src/pages/scene-editor.vue +++ b/src/pages/scene-editor.vue @@ -22,7 +22,7 @@ type Props = { const props = defineProps(); const { t } = useI18n(); -const { isConverting, convertSmapToScene, convertSceneToSmap, convertSceneToIray } = useMapConversion(); +const { isConverting, convertSmapToScene, exportSceneToSmap, convertSceneToIray } = useMapConversion(); //#region 接口 const readScene = async () => { @@ -152,27 +152,51 @@ const toPush = () => { }); }; -// --- Import Logic --- +// --- Import/Update Logic --- const importScene = async () => { const file = await selectFile('.scene'); - if (!file || !file.size) return; + if (!file) return; const json = await decodeTextFile(file); editor.value?.load(json, editable.value, undefined, true); }; const importSmap = async () => { const file = await selectFile('.smap'); - if (!file || !file.size) return; + if (!file) return; const sceneJson = await convertSmapToScene(file); if (sceneJson) { editor.value?.load(sceneJson, editable.value, undefined, true); } }; +const handleUpdateSceneWithSmap = async () => { + // 1. Get current scene content from editor + const currentSceneJson = editor.value?.save(); + if (!currentSceneJson) { + message.error('无法获取当前场景数据,请确保场景不为空'); + return; + } + + // 2. Prompt user to select the .smap file + message.info('请选择用于更新当前场景的 SMAP 文件'); + const smapFile = await selectFile('.smap'); + if (!smapFile) return; + + // 3. Convert current scene json to a File object in memory + const sceneBlob = new Blob([currentSceneJson], { type: 'application/json' }); + const sceneFile = new File([sceneBlob], `${title.value || 'current'}.scene`, { type: 'application/json' }); + + // 4. Call the update function + const newSceneJson = await convertSmapToScene(smapFile, sceneFile); + if (newSceneJson) { + editor.value?.load(newSceneJson, editable.value, undefined, true); + } +}; + const importBinTask = async () => { try { const file = await selectFile('.xlsx,.xls'); - if (!file || !file.size) return; + if (!file) return; const success = await importBinTaskExcel(props.id, file); if (success) { message.success('Bintask导入成功'); @@ -195,10 +219,23 @@ const exportScene = () => { URL.revokeObjectURL(url); }; -const exportAsSmap = async () => { - const json = editor.value?.save(); - if (!json) return; - await convertSceneToSmap(json, title.value || 'unknown'); +const exportSmap = async () => { + const sceneJson = editor.value?.save(); + if (!sceneJson) { + message.error('无法获取当前场景数据,请确保场景不为空'); + return; + } + + message.info('请选择一个基础 SMAP 文件用于导出'); + const smapFile = await selectFile('.smap'); + if (!smapFile) return; + + const sceneBlob = new Blob([sceneJson], { type: 'application/json' }); + const sceneFile = new File([sceneBlob], `${title.value || 'current'}.scene`, { + type: 'application/json', + }); + + await exportSceneToSmap(sceneFile, smapFile, smapFile.name.replace(/\.smap$/i, '')); }; const exportAsIray = async () => { @@ -285,8 +322,9 @@ const handleAutoCreateStorageCancel = () => { {{ $t('导入') }} @@ -295,7 +333,7 @@ const handleAutoCreateStorageCancel = () => { {{ $t('导出') }}