diff --git a/src/components/modal/ImportSmapModal.vue b/src/components/modal/ImportSmapModal.vue
index 7072209..cefc49f 100644
--- a/src/components/modal/ImportSmapModal.vue
+++ b/src/components/modal/ImportSmapModal.vue
@@ -1,8 +1,7 @@
@@ -50,7 +56,7 @@ const beforeUpload: UploadProps['beforeUpload'] = (file) => {
{
- 点击或拖拽 SMAP 文件到此区域
- 仅支持单个 .smap 文件的导入。
+ 点击或将 SMAP 文件拖拽到此处上传
+ 仅支持上传扩展名为 .smap 的文件。
- 保留原属性
+
+ 保留原有属性
+
diff --git a/src/components/modal/MapConverterModal.vue b/src/components/modal/MapConverterModal.vue
index c541295..7389399 100644
--- a/src/components/modal/MapConverterModal.vue
+++ b/src/components/modal/MapConverterModal.vue
@@ -2,11 +2,11 @@
-
+
- SMAP转Scene
- SMAP转IRAY
- Scene更新SMAP
+ SMAP 转 Scene
+ SMAP 转 IRAY
+ Scene 转 SMAP
@@ -16,11 +16,12 @@
@change="handleFile1Change"
:file-list="fileList1"
:max-count="1"
+ :accept="file1Accept"
>
- 点击或拖拽文件到这里上传
+ 点击或将文件拖拽到此处上传
@@ -30,16 +31,17 @@
@change="handleFile2Change"
:file-list="fileList2"
:max-count="1"
+ :accept="file2Accept"
>
- 点击或拖拽文件到这里上传
+ 点击或将文件拖拽到此处上传
-
地图参数设置 (IRAY转换必填)
+
地图参数设置(SMAP 转 IRAY)
@@ -54,12 +56,12 @@
-
+
-
+
@@ -68,14 +70,14 @@
{{
- convertType === 'iray' ? '上传、转换并下载' : '上传并转换'
+ convertType === 'iray' ? '上传并转换(返回压缩包)' : '上传并转换'
}}
下载转换后的文件
-
+
@@ -86,27 +88,23 @@
import { InboxOutlined } from '@ant-design/icons-vue';
import http from '@core/http';
import { message, type UploadChangeParam } from 'ant-design-vue';
-import { computed, reactive, ref, watch } from 'vue';
-/**
- * 调用地图转换API
- * @param convertType 转换类型
- * @param formData 包含文件和参数的FormData
- * @returns 转换结果
- */
-const convertMap = async (convertType: string, formData: FormData) => {
+import { computed, reactive, ref, watch, type Ref } from 'vue';
+
+type ConversionType = 'scene' | 'iray' | 'scene-to-smap';
+
+const convertMap = async (convertType: ConversionType, formData: FormData) => {
let apiUrl = '';
switch (convertType) {
case 'iray':
- apiUrl = import.meta.env.VWED_API_BASE_URL + '/api/vwed-map-converter/smap-to-iray';
+ apiUrl = `${import.meta.env.VWED_API_BASE_URL}/api/vwed-map-converter/smap-to-iray`;
break;
case 'scene-to-smap':
- apiUrl = import.meta.env.VWED_API_BASE_URL + '/api/vwed-map-converter/scene-to-smap';
+ apiUrl = `${import.meta.env.VWED_API_BASE_URL}/api/vwed-map-converter/scene-to-smap`;
break;
default:
- apiUrl = import.meta.env.VWED_API_BASE_URL + '/api/vwed-map-converter/smap-to-scene';
+ apiUrl = `${import.meta.env.VWED_API_BASE_URL}/api/vwed-map-converter/smap-to-scene`;
}
- // 如果是iray转换,需要特殊处理二进制响应
if (convertType === 'iray') {
const response = await http.post(apiUrl, formData, {
headers: { 'Content-Type': 'multipart/form-data' },
@@ -115,24 +113,23 @@ const convertMap = async (convertType: string, formData: FormData) => {
const blob = response?.data as unknown as Blob;
if (!blob) {
- throw new Error('返回的二进制文件为空');
+ throw new Error('返回的压缩包为空');
}
const disposition = response?.headers?.['content-disposition'] || '';
- let filename = 'map_package.zip'; // 默认文件名
+ let filename = 'map_package.zip';
const filenameMatch = disposition.match(/filename=([^;]+)/i);
if (filenameMatch && filenameMatch[1]) {
filename = decodeURIComponent(filenameMatch[1].replace(/"/g, ''));
}
return { blob, filename };
- } else {
- // 其他情况,保持原有的JSON响应处理
- return http.post(apiUrl, formData, {
- headers: { 'Content-Type': 'multipart/form-data' },
- });
}
+
+ return http.post(apiUrl, formData, {
+ headers: { 'Content-Type': 'multipart/form-data' },
+ });
};
-// Modal visibility
+
const visible = ref(false);
const handleCancel = () => {
visible.value = false;
@@ -143,50 +140,95 @@ const showModal = () => {
};
defineExpose({ showModal });
-// Component state
const loading = ref(false);
-const convertType = ref('scene');
+const convertType = ref('scene');
const file1 = ref(null);
const file2 = ref(null);
-const fileList1 = ref([]);
-const fileList2 = ref([]);
+const fileList1 = ref([]);
+const fileList2 = ref([]);
const result = ref('');
const downloadUrl = ref('');
const downloadFilename = ref('');
-const irayParams = reactive({
+const defaultIrayParams = {
mapWidth: 94744,
mapHeight: 79960,
xAttrMin: -46762,
yAttrMin: -63362,
-});
+};
+const irayParams = reactive({ ...defaultIrayParams });
-// UI labels based on convertType
const fileInputLabel = computed(() => {
if (convertType.value === 'scene-to-smap') {
- return '选择 .scene 文件 (必选)';
+ return '上传 Scene 文件(必选)';
}
- return '选择 .smap 文件 (必选)';
+ return '上传 SMAP 文件(必选)';
});
const sceneInputLabel = computed(() => {
if (convertType.value === 'iray') {
- return '选择 .scene 文件 (必选)';
+ return '上传 Scene 文件(必选)';
}
if (convertType.value === 'scene-to-smap') {
- return '选择 .smap 文件 (必选)';
+ return '上传 SMAP 文件(必选)';
}
- return '选择 .scene 文件 (可选)';
+ return '上传 Scene 文件(可选)';
});
-// File handling
+const file1Accept = computed(() => (convertType.value === 'scene-to-smap' ? '.scene' : '.smap'));
+const file2Accept = computed(() => (convertType.value === 'scene-to-smap' ? '.smap' : '.scene'));
+
+const file1ErrorMessage = computed(() =>
+ convertType.value === 'scene-to-smap' ? '请上传 .scene 格式的文件' : '请上传 .smap 格式的文件',
+);
+const file2ErrorMessage = computed(() =>
+ convertType.value === 'scene-to-smap' ? '请上传 .smap 格式的文件' : '请上传 .scene 格式的文件',
+);
+
+const parseAccept = (accept: string) =>
+ accept
+ .split(',')
+ .map((item) => item.trim().toLowerCase())
+ .filter(Boolean);
+
+const getFileExtension = (fileName: string) => {
+ const dotIndex = fileName.lastIndexOf('.');
+ return dotIndex >= 0 ? fileName.slice(dotIndex).toLowerCase() : '';
+};
+
+const isExtensionAllowed = (file: File, accept: string) => {
+ if (!accept) return true;
+ const allowed = parseAccept(accept);
+ if (!allowed.length) return true;
+ const ext = getFileExtension(file.name || '');
+ return allowed.includes(ext);
+};
+
+const updateFileSelection = (
+ info: UploadChangeParam,
+ fileRef: Ref,
+ listRef: Ref,
+ accept: string,
+ errorMessage: string,
+) => {
+ const rawFile = (info.file?.originFileObj as File | undefined) ?? null;
+
+ if (rawFile && !isExtensionAllowed(rawFile, accept)) {
+ message.error(errorMessage);
+ fileRef.value = null;
+ listRef.value = [];
+ return;
+ }
+
+ fileRef.value = rawFile;
+ listRef.value = info.fileList as UploadChangeParam['fileList'];
+};
+
const handleFile1Change = (info: UploadChangeParam) => {
- file1.value = (info.file as unknown as File) || null;
- fileList1.value = info.fileList as never[];
+ updateFileSelection(info, file1, fileList1, file1Accept.value, file1ErrorMessage.value);
};
const handleFile2Change = (info: UploadChangeParam) => {
- file2.value = (info.file as unknown as File) || null;
- fileList2.value = info.fileList as never[];
+ updateFileSelection(info, file2, fileList2, file2Accept.value, file2ErrorMessage.value);
};
const resetState = () => {
@@ -197,8 +239,10 @@ const resetState = () => {
fileList1.value = [];
fileList2.value = [];
result.value = '';
+ revokeDownloadUrl();
downloadUrl.value = '';
downloadFilename.value = '';
+ Object.assign(irayParams, defaultIrayParams);
};
watch(convertType, () => {
@@ -228,26 +272,26 @@ const handleDownload = () => {
document.body.removeChild(link);
};
-// Conversion logic
const handleConvert = async () => {
- // Validation
if (!file1.value) {
- message.error('请选择第一个文件');
+ message.error('请先上传主文件');
return;
}
if ((convertType.value === 'iray' || convertType.value === 'scene-to-smap') && !file2.value) {
- message.error('请选择第二个文件');
+ message.error('请上传辅助文件');
return;
}
if (convertType.value === 'iray') {
- if (!irayParams.mapWidth || !irayParams.mapHeight || !irayParams.xAttrMin || !irayParams.yAttrMin) {
- message.error('IRAY转换需要填写所有地图参数');
+ const { mapWidth, mapHeight, xAttrMin, yAttrMin } = irayParams;
+ if (!mapWidth || !mapHeight || xAttrMin === undefined || yAttrMin === undefined) {
+ message.error('IRAY 转换需要填写完整的地图参数');
return;
}
}
- // Revoke previous URL before creating a new one
revokeDownloadUrl();
+ downloadUrl.value = '';
+ downloadFilename.value = '';
const formData = new FormData();
if (convertType.value === 'scene-to-smap') {
@@ -267,52 +311,55 @@ const handleConvert = async () => {
loading.value = true;
result.value = '正在上传并转换...';
- downloadUrl.value = '';
try {
- // SMAP转IRAY时,API会返回二进制流,需要特殊处理
if (convertType.value === 'iray') {
- // convertMap内部已处理responseType,这里直接获取返回的blob和filename
const { blob, filename } = await convertMap(convertType.value, formData);
- console.log('blob', blob);
if (blob && blob.size > 0) {
- revokeDownloadUrl(); // 清理旧的URL
+ revokeDownloadUrl();
downloadUrl.value = URL.createObjectURL(blob);
- downloadFilename.value = filename;
- result.value = `转换成功!文件 ${filename} 已开始自动下载。`;
- message.success('IRAY地图包已生成,将自动开始下载。');
- // 不再显示下载按钮,而是直接触发下载
+ downloadFilename.value = filename || 'map_package.zip';
+ result.value = `转换成功,文件 ${downloadFilename.value} 已开始自动下载。`;
+ message.success('IRAY 地图转换成功,已自动开始下载。');
handleDownload();
- // 隐藏下载按钮,因为已经自动下载
setTimeout(() => {
+ revokeDownloadUrl();
downloadUrl.value = '';
}, 100);
} else {
- throw new Error('返回的二进制文件为空');
+ throw new Error('返回的压缩包为空');
}
} else {
- // 其他转换类型保持原有的JSON处理逻辑
const data = await convertMap(convertType.value, formData);
- if (data.code == 200 || data.success) {
- result.value = '转换成功!';
+ if (data?.code === 200 || data?.success) {
+ result.value = '转换成功。';
if (convertType.value === 'scene' && data.data) {
const sceneContent = JSON.stringify(data.data, null, 2);
const blob = new Blob([sceneContent], { type: 'application/json' });
+ revokeDownloadUrl();
downloadUrl.value = URL.createObjectURL(blob);
downloadFilename.value = data.data.output_filename || 'converted.scene';
+ message.success('转换成功,已生成 Scene 文件。');
} else if (convertType.value === 'scene-to-smap' && data.data) {
const smapContent = JSON.stringify(data.data?.data, null, 2);
const blob = new Blob([smapContent], { type: 'application/json' });
+ revokeDownloadUrl();
downloadUrl.value = URL.createObjectURL(blob);
- downloadFilename.value = data.data?.output_file ? data.data.output_file.split(/[/\\]/).pop() : 'updated.smap';
+ const outputFile = data.data?.output_file
+ ? data.data.output_file.split(/[/\\]/).pop()
+ : undefined;
+ downloadFilename.value = outputFile || 'updated.smap';
+ message.success('转换成功,已生成 SMAP 文件。');
}
} else {
- result.value = `转换失败: ${data.message || '未知错误'}`;
- message.error(`转换失败: ${data.message || '未知错误'}`);
+ const errorText = data?.message || '未知错误';
+ result.value = `转换失败:${errorText}`;
+ message.error(`转换失败:${errorText}`);
}
}
} catch (err) {
- result.value = '请求失败:' + err;
+ const errorMessage = err instanceof Error ? err.message : String(err);
+ result.value = `处理失败:${errorMessage}`;
message.error('转换失败');
} finally {
loading.value = false;