feat: 更新场景转换功能,支持在导出时选择 SMAP 文件,优化导入 SMAP 的用户交互和提示信息
This commit is contained in:
parent
318775a6e0
commit
b810c7560f
75
src/components/modal/ImportSmapModal.vue
Normal file
75
src/components/modal/ImportSmapModal.vue
Normal file
@ -0,0 +1,75 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, type Ref } from 'vue';
|
||||
import { CloudUploadOutlined } from '@ant-design/icons-vue';
|
||||
import type { UploadProps } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
type Props = {
|
||||
visible: boolean;
|
||||
};
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const emit = defineEmits(['update:visible', 'confirm']);
|
||||
|
||||
const fileList: Ref<UploadProps['fileList']> = ref([]);
|
||||
const keepProperties = ref(false);
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(newVal) => {
|
||||
if (!newVal) {
|
||||
// Reset state when modal is closed
|
||||
fileList.value = [];
|
||||
keepProperties.value = false;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const handleOk = () => {
|
||||
if (fileList.value && fileList.value.length > 0) {
|
||||
const smapFile = fileList.value[0].originFileObj as File;
|
||||
emit('confirm', { smapFile, keepProperties: keepProperties.value });
|
||||
emit('update:visible', false);
|
||||
} else {
|
||||
message.error('请选择一个 SMAP 文件');
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('update:visible', false);
|
||||
};
|
||||
|
||||
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
||||
// Ensure only one file is in the list
|
||||
fileList.value = [file];
|
||||
// Prevent automatic upload
|
||||
return false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal
|
||||
:visible="props.visible"
|
||||
title="从 SMAP 导入场景"
|
||||
ok-text="开始导入"
|
||||
cancel-text="取消"
|
||||
:ok-button-props="{ disabled: !fileList || fileList.length === 0 }"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-upload-dragger
|
||||
v-model:fileList="fileList"
|
||||
name="file"
|
||||
:multiple="false"
|
||||
accept=".smap"
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<p class="ant-upload-drag-icon">
|
||||
<cloud-upload-outlined />
|
||||
</p>
|
||||
<p class="ant-upload-text">点击或拖拽 SMAP 文件到此区域</p>
|
||||
<p class="ant-upload-hint">仅支持单个 .smap 文件的导入。</p>
|
||||
</a-upload-dragger>
|
||||
<a-checkbox v-model:checked="keepProperties" style="margin-top: 16px"> 保留原属性 </a-checkbox>
|
||||
</a-modal>
|
||||
</template>
|
@ -90,14 +90,15 @@ export function useMapConversion() {
|
||||
}
|
||||
};
|
||||
|
||||
const convertSceneToIray = async (sceneJson: string, filename: string) => {
|
||||
const convertSceneToIray = async (sceneJson: string, 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('smap_file', smapFile);
|
||||
|
||||
const response = await mapConverterHttp.post(`${API_BASE_URL}/scene-to-iray`, formData, {
|
||||
const response = await mapConverterHttp.post(`${API_BASE_URL}/smap-to-iray`, formData, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
responseType: 'blob',
|
||||
});
|
||||
|
@ -8,6 +8,7 @@ import expandIcon from '../assets/icons/png/expand.png';
|
||||
import foldIcon from '../assets/icons/png/fold.png';
|
||||
import BatchEditToolbar from '../components/batch-edit-toolbar.vue';
|
||||
import AutoCreateStorageModal from '../components/modal/auto-create-storage-modal.vue';
|
||||
import ImportSmapModal from '../components/modal/ImportSmapModal.vue';
|
||||
import { useMapConversion } from '../hooks/useMapConversion';
|
||||
import { EditorService } from '../services/editor.service';
|
||||
import { useViewState } from '../services/useViewState';
|
||||
@ -160,39 +161,38 @@ const importScene = async () => {
|
||||
editor.value?.load(json, editable.value, undefined, true);
|
||||
};
|
||||
|
||||
const importSmap = async () => {
|
||||
const file = await selectFile('.smap');
|
||||
if (!file) return;
|
||||
const sceneJson = await convertSmapToScene(file);
|
||||
const importSmapModalVisible = ref(false);
|
||||
|
||||
const handleImportSmapConfirm = async ({
|
||||
smapFile,
|
||||
keepProperties,
|
||||
}: {
|
||||
smapFile: File;
|
||||
keepProperties: boolean;
|
||||
}) => {
|
||||
let sceneJson: string | null = null;
|
||||
if (keepProperties) {
|
||||
// Update mode
|
||||
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',
|
||||
});
|
||||
sceneJson = await convertSmapToScene(smapFile, sceneFile);
|
||||
} else {
|
||||
// Create mode
|
||||
sceneJson = await convertSmapToScene(smapFile);
|
||||
}
|
||||
|
||||
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');
|
||||
@ -240,8 +240,16 @@ const exportSmap = async () => {
|
||||
|
||||
const exportAsIray = async () => {
|
||||
const json = editor.value?.save();
|
||||
if (!json) return;
|
||||
await convertSceneToIray(json, title.value || 'unknown');
|
||||
if (!json) {
|
||||
message.error('无法获取当前场景数据,请确保场景不为空');
|
||||
return;
|
||||
}
|
||||
|
||||
message.info('请选择一个 SMAP 文件用于 IRAY 导出');
|
||||
const smapFile = await selectFile('.smap');
|
||||
if (!smapFile) return;
|
||||
|
||||
await convertSceneToIray(json, smapFile, title.value || 'unknown');
|
||||
};
|
||||
|
||||
const show = ref<boolean>(true);
|
||||
@ -322,9 +330,8 @@ 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="handleUpdateSceneWithSmap">通过 SMAP 更新 Scene</a-menu-item>
|
||||
<a-menu-item key="3" @click="importBinTask">导入 Bintask (.xlsx)</a-menu-item>
|
||||
<a-menu-item key="1" @click="importSmapModalVisible = true">导入其他格式 (.smap)</a-menu-item>
|
||||
<a-menu-item key="2" @click="importBinTask">导入 Bintask (.xlsx)</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown-button>
|
||||
@ -387,6 +394,8 @@ const handleAutoCreateStorageCancel = () => {
|
||||
|
||||
<BatchEditToolbar v-if="editable && editor" :token="EDITOR_KEY" />
|
||||
|
||||
<ImportSmapModal v-model:visible="importSmapModalVisible" @confirm="handleImportSmapConfirm" />
|
||||
|
||||
<AutoCreateStorageModal
|
||||
v-model:visible="autoCreateStorageVisible"
|
||||
:area-name="autoCreateStorageData.areaName"
|
||||
|
Loading…
x
Reference in New Issue
Block a user