feat: 更新场景转换功能,支持通过 SMAP 文件更新场景,重命名导出函数并优化用户提示信息
This commit is contained in:
parent
b5a3fd761c
commit
318775a6e0
@ -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<string | null> => {
|
||||
/**
|
||||
* 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<string | null> => {
|
||||
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<any>(`${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<any>(`${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,
|
||||
};
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ type Props = {
|
||||
const props = defineProps<Props>();
|
||||
|
||||
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('导入') }}
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="importSmap">导入 SMAP (.smap)</a-menu-item>
|
||||
<a-menu-item key="2" @click="importBinTask">导入 Bintask (.xlsx)</a-menu-item>
|
||||
<a-menu-item key="1" @click="importSmap">从 SMAP 新建 (.smap)</a-menu-item>
|
||||
<a-menu-item key="2" @click="handleUpdateSceneWithSmap">通过 SMAP 更新 Scene</a-menu-item>
|
||||
<a-menu-item key="3" @click="importBinTask">导入 Bintask (.xlsx)</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown-button>
|
||||
@ -295,7 +333,7 @@ const handleAutoCreateStorageCancel = () => {
|
||||
{{ $t('导出') }}
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="exportAsSmap">导出为 SMAP (.smap)</a-menu-item>
|
||||
<a-menu-item key="1" @click="exportSmap">导出为 SMAP (.smap)</a-menu-item>
|
||||
<a-menu-item key="2" @click="exportAsIray">导出为 IRAY (.zip)</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
|
Loading…
x
Reference in New Issue
Block a user