From 259c881469845a5e8748027b7f47f57fdefa0a89 Mon Sep 17 00:00:00 2001 From: xudan Date: Tue, 21 Oct 2025 14:06:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(scene-editor):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=AF=B9=E6=8E=A5=20JSZip=20=E5=BA=93=E4=BB=A5=E6=94=AF?= =?UTF-8?q?=E6=8C=81=20ZIP=20=E6=96=87=E4=BB=B6=E5=AF=BC=E5=85=A5=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AF=BC=E5=85=A5=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + pnpm-lock.yaml | 70 ++++++++++++++++++++++++++++++++++++++ src/pages/scene-editor.vue | 51 ++++++++++++++++++++------- 3 files changed, 109 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index a7430cd..722ae31 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "ant-design-vue": "^4.2.6", "axios": "^1.8.4", "dayjs": "^1.11.13", + "jszip": "^3.10.1", "lodash-es": "^4.17.21", "pinia": "^3.0.3", "rxjs": "^7.8.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 24e01e5..dfae303 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: dayjs: specifier: ^1.11.13 version: 1.11.13 + jszip: + specifier: ^3.10.1 + version: 3.10.1 lodash-es: specifier: ^4.17.21 version: 4.17.21 @@ -1004,6 +1007,9 @@ packages: core-js@3.45.1: resolution: {integrity: sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==} + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cosmiconfig@9.0.0: resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} engines: {node: '>=14'} @@ -1380,6 +1386,9 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + immutable@5.1.3: resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==} @@ -1436,6 +1445,9 @@ packages: resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} engines: {node: '>=12.13'} + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -1467,6 +1479,9 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -1491,6 +1506,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -1630,6 +1648,9 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -1765,6 +1786,9 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -1813,6 +1837,9 @@ packages: rxjs@7.8.2: resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -1946,6 +1973,9 @@ packages: engines: {node: '>=10'} hasBin: true + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + shallow-equal@1.2.1: resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==} @@ -1987,6 +2017,9 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -3128,6 +3161,8 @@ snapshots: core-js@3.45.1: {} + core-util-is@1.0.3: {} + cosmiconfig@9.0.0(typescript@5.7.3): dependencies: env-paths: 2.2.1 @@ -3535,6 +3570,8 @@ snapshots: ignore@7.0.5: {} + immediate@3.0.6: {} + immutable@5.1.3: {} import-fresh@3.3.1: @@ -3575,6 +3612,8 @@ snapshots: is-what@4.1.16: {} + isarray@1.0.0: {} + isexe@2.0.0: {} js-sdsl@4.3.0: {} @@ -3597,6 +3636,13 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + jszip@3.10.1: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -3618,6 +3664,10 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lie@3.3.0: + dependencies: + immediate: 3.0.6 + lines-and-columns@1.2.4: {} local-pkg@1.1.2: @@ -3773,6 +3823,8 @@ snapshots: dependencies: p-limit: 3.1.0 + pako@1.0.11: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -3887,6 +3939,16 @@ snapshots: queue-microtask@1.2.3: {} + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -3948,6 +4010,8 @@ snapshots: dependencies: tslib: 2.8.1 + safe-buffer@5.1.2: {} + safe-buffer@5.2.1: {} sass-embedded-all-unknown@1.91.0: @@ -4053,6 +4117,8 @@ snapshots: semver@7.7.2: {} + setimmediate@1.0.5: {} + shallow-equal@1.2.1: {} shebang-command@2.0.0: @@ -4087,6 +4153,10 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 diff --git a/src/pages/scene-editor.vue b/src/pages/scene-editor.vue index b243bfe..235dcbd 100644 --- a/src/pages/scene-editor.vue +++ b/src/pages/scene-editor.vue @@ -2,6 +2,7 @@ // 门区域功能改造:由助手自动插入 import { DOOR_AREA_TYPE } from '@api/map/door-area'; import { message, Modal } from 'ant-design-vue'; +import JSZip from 'jszip'; import { computed, onMounted, onUnmounted, provide, ref, type ShallowRef, shallowRef, watch } from 'vue'; import { useI18n } from 'vue-i18n'; @@ -287,7 +288,7 @@ const toPush = () => { // --- Import/Update Logic --- const importModalVisible = ref(false); -const importMode = ref<'normal' | 'floor'>('normal'); +const importMode = ref<'normal' | 'floor'>('floor'); const normalImportFileList = ref([]); const normalImportKeepProperties = ref(true); const floorImportList = ref([{ name: 'F1', fileList: [], keepProperties: true }]); @@ -319,13 +320,16 @@ const addFloor = () => { }; const openImportModal = () => { - importMode.value = 'normal'; + importMode.value = 'floor'; // 默认使用分区域模式 importModalVisible.value = true; + // 打开时重置为文件选择界面 + floorImportList.value = [{ name: 'F1', fileList: [], keepProperties: true }]; OriginalProperties.value = []; }; const handleImportConfirm = async () => { try { + /* NORMAL_IMPORT_DISABLED if (importMode.value === 'normal') { if (normalImportFileList.value.length === 0) { message.warn('请选择一个文件'); @@ -366,7 +370,9 @@ const handleImportConfirm = async () => { return; } await processAndLoadSceneData(JSON.parse(sceneJsonString)); - } else if (importMode.value === 'floor') { + } + NORMAL_IMPORT_DISABLED */ + if (importMode.value === 'floor') { // === 新增:对接地图转换服务(含 OriginalProperties 采集) === try { if (floorImportList.value.some((f) => f.fileList.length === 0)) { @@ -402,11 +408,30 @@ const handleImportConfirm = async () => { // 3) 调用转换服务 const result = await postProcessJsonList(payload); if (result.kind === 'zip') { - const url = URL.createObjectURL(result.blob); - const filename = result.filename || 'converted_map.zip'; - downloadFile(url, filename); - URL.revokeObjectURL(url); - message.success('转换成功,已下载ZIP结果'); + try { + const zip = await JSZip.loadAsync(result.blob); + const sceneFile = Object.values(zip.files).find((file) => file.name.toLowerCase().endsWith('.scene')); + + if (sceneFile) { + const sceneJsonString = await sceneFile.async('string'); + if (!sceneJsonString) { + message.error('ZIP中的 .scene 文件内容为空'); + return; + } + await processAndLoadSceneData(JSON.parse(sceneJsonString)); + message.success('已成功从ZIP包中解压并导入场景'); + } else { + message.error('ZIP文件中未找到 .scene 格式的场景文件'); + // 作为回退,仍然提供下载功能 + const url = URL.createObjectURL(result.blob); + const filename = result.filename || 'converted_map.zip'; + downloadFile(url, filename); + URL.revokeObjectURL(url); + } + } catch (zipError) { + console.error('处理ZIP文件失败:', zipError); + message.error('自动解压导入失败,请检查ZIP文件是否有效'); + } } else { message.success(result?.data?.message || '转换成功'); console.log('MapConverter JSON 响应:', result.data); @@ -765,12 +790,12 @@ const handleFloorChange = async (value: any) => {
-
+
-
+ -
+